diff --git a/Cargo.lock b/Cargo.lock index 873182dde9102..9d5d0f2d403f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6213,9 +6213,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "44.0.2" +version = "46.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2211ca2d69a88055eefb06bad741dde3180c9d4020f7e42fea72caba83f9c10c" +checksum = "1b6c48003fe59c201c97a7786ff55feabe6b6f83b598aa9ff5bcc4f94d940bf3" [[package]] name = "wasip2" @@ -6282,9 +6282,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.25" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee72c06c556db23aca2d29e1e183d96efdffa7110b24915629a5091de600a894" +checksum = "51a12709376d4ce64f472699500db3b0e5902cc2bef16fb6ca3098bfdac032fa" dependencies = [ "anyhow", "clap", @@ -6293,12 +6293,12 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.252.0", + "wasmparser 0.253.0", "wat", "windows-sys 0.61.2", "winsplit", - "wit-component 0.252.0", - "wit-parser 0.252.0", + "wit-component 0.253.0", + "wit-parser 0.253.0", ] [[package]] @@ -6330,12 +6330,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f" +checksum = "59972d6cd272259de647b7c1f1912e45e289c75ffd4be04e10695507cd7e1b59" dependencies = [ "leb128fmt", - "wasmparser 0.252.0", + "wasmparser 0.253.0", ] [[package]] @@ -6352,14 +6352,14 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7e08e02a3cd55bf778009d4cd6faae50da011f293644daf78a531a32d6d142" +checksum = "b3f45816ef616806f48498bcd831377de578c4fa51db0c83ab8ceb78cc13523b" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.252.0", - "wasmparser 0.252.0", + "wasm-encoder 0.253.0", + "wasmparser 0.253.0", ] [[package]] @@ -6396,9 +6396,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c" +checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339" dependencies = [ "bitflags", "hashbrown 0.17.0", @@ -6409,22 +6409,22 @@ dependencies = [ [[package]] name = "wast" -version = "252.0.0" +version = "253.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942a3449d6a593fccc111a6241c8df52bda168af30e40bf9580d4394d7374c65" +checksum = "d3264542f8965c5d84fb1085d924bfba9a6314bb228eff13a2de14d7627664d0" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.252.0", + "wasm-encoder 0.253.0", ] [[package]] name = "wat" -version = "1.252.0" +version = "1.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c72a4ba7088f7bac94cf516e49882bdf97068904a563768cf249efc839ec42cb" +checksum = "4bfc5ce906144200c972ec617470aa35bd847472e170b26dde3e80541c674055" dependencies = [ "wast", ] @@ -6872,9 +6872,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76db0662b590f45d33d0e363fa13539a5a1eecd35d5a12fe208c335461c1053d" +checksum = "dbbd2500ac3488489ee8c6e59b79d7e47e6da5bfb019efd35d5dca57b78af624" dependencies = [ "anyhow", "bitflags", @@ -6883,10 +6883,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.252.0", - "wasm-metadata 0.252.0", - "wasmparser 0.252.0", - "wit-parser 0.252.0", + "wasm-encoder 0.253.0", + "wasm-metadata 0.253.0", + "wasmparser 0.253.0", + "wit-parser 0.253.0", ] [[package]] @@ -6909,9 +6909,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4266bea110371c620ccf3201c5023676046bc4556e5c7cfb5d500bda5ebc162d" +checksum = "4d997b8e5920fcbeec742b58e583325d6419a6aca617ae8075c406a61c65ba8a" dependencies = [ "anyhow", "hashbrown 0.17.0", @@ -6923,7 +6923,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-ident", - "wasmparser 0.252.0", + "wasmparser 0.253.0", ] [[package]] diff --git a/RELEASES.md b/RELEASES.md index 2fa271401fbb3..3146bdeb1b118 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,93 @@ +Version 1.97.0 (2026-07-09) +========================== + + + +Language +-------- +- [Consider `Result` and `ControlFlow` to be equivalent to `T` for must use lint](https://github.com/rust-lang/rust/pull/148214) +- [Add allow-by-default `dead_code_pub_in_binary` lint for unused pub items in binary crates](https://github.com/rust-lang/rust/pull/149509) +- [Stabilize the `div32`, `lam-bh`, `lamcas`, `ld-seq-sa` and `scq` target features](https://github.com/rust-lang/rust/pull/154510) +- [Stabilize `cfg(target_has_atomic_primitive_alignment)`](https://github.com/rust-lang/rust/pull/155006) +- [Allow trailing `self` in imports in more cases](https://github.com/rust-lang/rust/pull/155137) + + + + +Platform Support +---------------- +- [nvptx64-nvidia-cuda: drop support for old architectures and old ISAs](https://github.com/rust-lang/rust/pull/152443) + + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + + +Stabilized APIs +--------------- + +- [`Default for RepeatN`](https://doc.rust-lang.org/stable/std/iter/struct.RepeatN.html#impl-Default-for-RepeatN%3CA%3E) +- [`Copy for ffi::FromBytesUntilNulError`](https://doc.rust-lang.org/stable/std/ffi/struct.FromBytesUntilNulError.html#impl-Copy-for-FromBytesUntilNulError) +- [`Send for std::fs::File` on UEFI](https://github.com/rust-lang/rust/pull/154003) +- [`<{integer}>::isolate_highest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.isolate_highest_one) +- [`<{integer}>::isolate_lowest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.isolate_lowest_one) +- [`<{integer}>::highest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.highest_one) +- [`<{integer}>::lowest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.lowest_one) +- [`<{integer}>::bit_width`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.bit_width) +- [`NonZero<{integer}>::isolate_highest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.isolate_highest_one) +- [`NonZero<{integer}>::isolate_lowest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.isolate_lowest_one) +- [`NonZero<{integer}>::highest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.highest_one) +- [`NonZero<{integer}>::lowest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.lowest_one) +- [`NonZero<{integer}>::bit_width`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.bit_width) + + +These previously stable APIs are now stable in const contexts: + +- [`char::is_control`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_control) + + + + +Cargo +----- +- [Stabilize `build.warnings` config.](https://github.com/rust-lang/cargo/pull/16796) This controls how lint warnings from local packages are treated. Useful for enforcing a warning-free build in CI, replacing `-Dwarnings`. [docs](https://doc.rust-lang.org/nightly/cargo/reference/config.html#buildwarnings) +- [Stabilize `resolver.lockfile-path` config.](https://github.com/rust-lang/cargo/pull/16694) This allows specifying the path to the lockfile to use when resolving dependencies. Useful when working with read-only source directories. [docs](https://doc.rust-lang.org/nightly/cargo/reference/config.html#resolverlockfile-path) +- [cargo-clean: Error when `--target-dir` doesn't look like a Cargo target directory.](https://github.com/rust-lang/cargo/pull/16712) This prevents accidental deletion of non-target directories. +- [Add `-m` shorthand for `--manifest-path`](https://github.com/rust-lang/cargo/pull/16858) +- [Remove `curl` dependency from `crates-io` crate](https://github.com/rust-lang/cargo/pull/16936) + + + +Rustdoc +----- +- [Stabilize `--emit` flag](https://github.com/rust-lang/rust/pull/146220) +- [Stabilize `--remap-path-prefix`](https://github.com/rust-lang/rust/pull/155307) + + + + +Compatibility Notes +------------------- +- [Emit a future-compatibility warning when relying on `f32: From<{float}>` to constrain `{float}`](https://github.com/rust-lang/rust/pull/139087) +- [Rust will use the v0 symbol mangling scheme by default.](https://github.com/rust-lang/rust/pull/151994) This may cause some tools (such as debuggers or profilers, especially with old versions) to fail to demangle symbols emitted by Rust. It may also cause the formatting of text in backtraces to change. +- [Prevent deref coercions in `pin!`, in order to prevent unsoundness.](https://github.com/rust-lang/rust/pull/153457) The most likely case where this might impact users is: writing `pin!(x)` where `x` has type `&mut T` will now always correctly produce a value of type `Pin<&mut &mut T>`, instead of sometimes allowing a coercion that produces a value of type `Pin<&mut T>`. This coercion was previously incorrectly allowed since Rust 1.88.0. +- [Deprecate `std::char` constants and functions](https://github.com/rust-lang/rust/pull/153873) +- [Warn on linker output by default](https://github.com/rust-lang/rust/pull/153968) +- [Remove hidden `f64` methods which have been deprecated since 1.0](https://github.com/rust-lang/rust/pull/153975) +- [report the `varargs_without_pattern` lint in deps](https://github.com/rust-lang/rust/pull/154599) +- [Forbid passing generic arguments to module path segments even if the module reexports a generic enum variant](https://github.com/rust-lang/rust/pull/154971) +- [Error on invalid macho `link_section` specifier](https://github.com/rust-lang/rust/pull/155065) +- The encoding of certain `enum`s [have changed](https://github.com/rust-lang/rust/pull/155473). This is not a breaking change, as it only applies to `enum`s without layout guarantees, but is noted here as we've seen people impacted from having made assumptions about the layout algorithm. +- [Error on `#[export_name = "..."]` where the name is empty](https://github.com/rust-lang/rust/pull/155515) +- [Syntactically reject tuple index shorthands in struct patterns](https://github.com/rust-lang/rust/pull/155698) +- [validate `#[link_name = "..."]` & `#[link(name = "...")]` parameters](https://github.com/rust-lang/rust/pull/155817) +- On Windows, after calling `shutdown` on a socket to shut down the write side, attempting to write to the socket will now produce a `BrokenPipe` error rather than `Other`. [Map `WSAESHUTDOWN` to `io::ErrorKind::BrokenPipe`](https://github.com/rust-lang/rust/pull/156063) + + Version 1.96.1 (2026-06-30) =========================== diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 41e87caf40c7d..4fda4735b613c 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -89,7 +89,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { unreachable!("`homogeneous_aggregate` should not be called for scalable vectors") } - BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { sized: true } => { // Helper for computing `homogeneous_aggregate`, allowing a custom // starting offset (used below for handling variants). let from_fields_at = diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 003e811dc7855..ad48e14430952 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -476,7 +476,7 @@ impl LayoutCalculator { Err(AbiMismatch) | Ok(None) => BackendRepr::Memory { sized: true }, Ok(Some((repr, _))) => match repr { // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt - BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) + BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } if repr.scalar_platform_align(dl).unwrap() != align => { BackendRepr::Memory { sized: true } @@ -489,7 +489,7 @@ impl LayoutCalculator { } // the alignment tests passed and we can use this BackendRepr::Scalar(..) - | BackendRepr::ScalarPair(..) + | BackendRepr::ScalarPair { .. } | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } | BackendRepr::Memory { .. } => repr, @@ -558,7 +558,7 @@ impl LayoutCalculator { }; match &mut st.backend_repr { BackendRepr::Scalar(scalar) => hide_niches(scalar), - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { hide_niches(a); hide_niches(b); } @@ -701,13 +701,21 @@ impl LayoutCalculator { // When the total alignment and size match, we can use the // same ABI as the scalar variant with the reserved niche. BackendRepr::Scalar(_) => BackendRepr::Scalar(niche_scalar), - BackendRepr::ScalarPair(first, second) => { + BackendRepr::ScalarPair { a: first, b: second, b_offset } => { // Only the niche is guaranteed to be initialised, // so use union layouts for the other primitive. if niche_offset == Size::ZERO { - BackendRepr::ScalarPair(niche_scalar, second.to_union()) + BackendRepr::ScalarPair { + a: niche_scalar, + b: second.to_union(), + b_offset, + } } else { - BackendRepr::ScalarPair(first.to_union(), niche_scalar) + BackendRepr::ScalarPair { + a: first.to_union(), + b: niche_scalar, + b_offset, + } } } _ => BackendRepr::Memory { sized: true }, @@ -1037,7 +1045,7 @@ impl LayoutCalculator { // If we pick a "clever" (by-value) ABI, we might have to adjust the ABI of the // variants to ensure they are consistent. This is because a downcast is // semantically a NOP, and thus should not affect layout. - if matches!(abi, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) { + if matches!(abi, BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }) { for variant in &mut layout_variants { // We only do this for variants with fields; the others are not accessed anyway. // Also do not overwrite any already existing "clever" ABIs. @@ -1354,7 +1362,7 @@ impl LayoutCalculator { } // But scalar pairs are Rust-specific and get // treated as aggregates by C ABIs anyway. - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { abi = field.backend_repr; } _ => {} diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs index 5fd504def384a..1fffb84ec21cb 100644 --- a/compiler/rustc_abi/src/layout/simple.rs +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -110,7 +110,7 @@ impl LayoutData { offsets: [Size::ZERO, b_offset].into(), in_memory_order: [FieldIdx::new(0), FieldIdx::new(1)].into(), }, - backend_repr: BackendRepr::ScalarPair(a, b), + backend_repr: BackendRepr::ScalarPair { a, b, b_offset }, largest_niche, uninhabited: false, align: AbiAlign::new(align), diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index c2069432e6f8e..b8cd152e30288 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1796,14 +1796,18 @@ impl IntoDiagArg for NumScalableVectors { pub enum BackendRepr { Scalar(Scalar), /// The data contained in this type can be entirely represented by two scalars. - /// The two scalars are listed in *memory* order, so the first is at offset zero - /// and the second at a non-zero offset. + /// The two scalars are listed in *memory* order, so `a` is at offset zero + /// and `b` is at non-zero offset `b_offset`. /// These need not be `FieldIdx(0)` and `FieldIdx(1)`. /// - /// As of June 2026 the offset to the second scalar is the size of the first - /// scalar rounded up to the platform alignment of the second scalar. + /// As of June 2026 the `b_offset` is always the size of the `a` + /// scalar rounded up to the platform alignment of the `b` scalar. /// That may soon change, however; see MCP#1007. - ScalarPair(Scalar, Scalar), + ScalarPair { + a: Scalar, + b: Scalar, + b_offset: Size, + }, SimdScalableVector { element: Scalar, count: u64, @@ -1826,7 +1830,7 @@ impl BackendRepr { pub fn is_unsized(&self) -> bool { match *self { BackendRepr::Scalar(_) - | BackendRepr::ScalarPair(..) + | BackendRepr::ScalarPair { .. } // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is // fully implemented, scalable vectors will remain `Sized`, they just won't be @@ -1875,7 +1879,7 @@ impl BackendRepr { pub fn scalar_platform_align(&self, cx: &C) -> Option { match *self { BackendRepr::Scalar(s) => Some(s.default_align(cx).abi), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { Some(s1.default_align(cx).max(s2.default_align(cx)).abi) } // The align of a Vector can vary in surprising ways @@ -1893,8 +1897,7 @@ impl BackendRepr { // No padding in scalars. BackendRepr::Scalar(s) => Some(s.size(cx)), // May have some padding between the pair. - BackendRepr::ScalarPair(s1, s2) => { - let field2_offset = s1.size(cx).align_to(s2.default_align(cx).abi); + BackendRepr::ScalarPair { a: _, b: s2, b_offset: field2_offset } => { let size = (field2_offset + s2.size(cx)).align_to( self.scalar_platform_align(cx) // We absolutely must have an answer here or everything is FUBAR. @@ -1913,8 +1916,8 @@ impl BackendRepr { pub fn to_union(&self) -> Self { match *self { BackendRepr::Scalar(s) => BackendRepr::Scalar(s.to_union()), - BackendRepr::ScalarPair(s1, s2) => { - BackendRepr::ScalarPair(s1.to_union(), s2.to_union()) + BackendRepr::ScalarPair { a: s1, b: s2, b_offset } => { + BackendRepr::ScalarPair { a: s1.to_union(), b: s2.to_union(), b_offset } } BackendRepr::SimdVector { element, count } => { BackendRepr::SimdVector { element: element.to_union(), count } @@ -1939,8 +1942,13 @@ impl BackendRepr { BackendRepr::SimdVector { element: element_l, count: count_l }, BackendRepr::SimdVector { element: element_r, count: count_r }, ) => element_l.primitive() == element_r.primitive() && count_l == count_r, - (BackendRepr::ScalarPair(l1, l2), BackendRepr::ScalarPair(r1, r2)) => { - l1.primitive() == r1.primitive() && l2.primitive() == r2.primitive() + ( + BackendRepr::ScalarPair { a: l1, b: l2, b_offset: l_offset }, + BackendRepr::ScalarPair { a: r1, b: r2, b_offset: r_offset }, + ) => { + l1.primitive() == r1.primitive() + && l2.primitive() == r2.primitive() + && l_offset == r_offset } // Everything else must be strictly identical. _ => self == other, @@ -2180,7 +2188,7 @@ impl LayoutData { BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => false, - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => true, + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => true, } } @@ -2294,7 +2302,7 @@ impl LayoutData { pub fn is_zst(&self) -> bool { match self.backend_repr { BackendRepr::Scalar(_) - | BackendRepr::ScalarPair(..) + | BackendRepr::ScalarPair { .. } | BackendRepr::SimdScalableVector { .. } | BackendRepr::SimdVector { .. } => false, BackendRepr::Memory { sized } => sized && self.size.bytes() == 0, diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 012951098fa15..ec17e72900dfb 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -220,7 +220,7 @@ fn make_local_place<'tcx>( ); } let place = if is_ssa { - if let BackendRepr::ScalarPair(_, _) = layout.backend_repr { + if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = layout.backend_repr { CPlace::new_var_pair(fx, local, layout) } else { CPlace::new_var(fx, local, layout) diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 612f89e6a4217..75d7e4d9fd500 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -112,7 +112,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.backend_repr), }, PassMode::Pair(attrs_a, attrs_b) => match self.layout.backend_repr { - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { let a = scalar_to_clif_type(tcx, a); let b = scalar_to_clif_type(tcx, b); smallvec![ @@ -167,7 +167,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.backend_repr), }, PassMode::Pair(attrs_a, attrs_b) => match self.layout.backend_repr { - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { let a = scalar_to_clif_type(tcx, a); let b = scalar_to_clif_type(tcx, b); ( diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index b37fb213d945a..bf6a98866cd12 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -695,7 +695,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: } UnOp::PtrMetadata => match layout.backend_repr { BackendRepr::Scalar(_) => CValue::zst(dest_layout), - BackendRepr::ScalarPair(_, _) => { + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => { CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout) } _ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"), diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index e6b8c537d8451..d4bb8bd019331 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -572,7 +572,9 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = fx.layout_of(generic_args.type_at(0)); // Note: Can't use is_unsized here as truly unsized types need to take the fixed size // branch - let meta = if let BackendRepr::ScalarPair(_, _) = ptr.layout().backend_repr { + let meta = if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + ptr.layout().backend_repr + { Some(ptr.load_scalar_pair(fx).1) } else { None @@ -586,7 +588,9 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = fx.layout_of(generic_args.type_at(0)); // Note: Can't use is_unsized here as truly unsized types need to take the fixed size // branch - let meta = if let BackendRepr::ScalarPair(_, _) = ptr.layout().backend_repr { + let meta = if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + ptr.layout().backend_repr + { Some(ptr.load_scalar_pair(fx).1) } else { None diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 995a70f24240b..d7248acfafef1 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -55,8 +55,7 @@ fn codegen_field<'tcx>( } } -fn scalar_pair_calculate_b_offset(tcx: TyCtxt<'_>, a_scalar: Scalar, b_scalar: Scalar) -> Offset32 { - let b_offset = a_scalar.size(&tcx).align_to(b_scalar.default_align(&tcx).abi); +fn scalar_pair_convert_b_offset(b_offset: Size) -> Offset32 { Offset32::new(b_offset.bytes().try_into().unwrap()) } @@ -159,11 +158,11 @@ impl<'tcx> CValue<'tcx> { let layout = self.1; match self.0 { CValueInner::ByRef(ptr, None) => { - let (a_scalar, b_scalar) = match layout.backend_repr { - BackendRepr::ScalarPair(a, b) => (a, b), + let (a_scalar, b_scalar, b_offset) = match layout.backend_repr { + BackendRepr::ScalarPair { a, b, b_offset } => (a, b, b_offset), _ => unreachable!("load_scalar_pair({:?})", self), }; - let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + let b_offset = scalar_pair_convert_b_offset(b_offset); let clif_ty1 = scalar_to_clif_type(fx.tcx, a_scalar); let clif_ty2 = scalar_to_clif_type(fx.tcx, b_scalar); let mut flags = MemFlags::new(); @@ -189,7 +188,7 @@ impl<'tcx> CValue<'tcx> { match self.0 { CValueInner::ByVal(_) => unreachable!(), CValueInner::ByValPair(val1, val2) => match layout.backend_repr { - BackendRepr::ScalarPair(_, _) => { + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => { let val = match field.as_u32() { 0 => val1, 1 => val2, @@ -580,7 +579,7 @@ impl<'tcx> CPlace<'tcx> { } CPlaceInner::VarPair(_local, var1, var2) => { let (data1, data2) = match from.1.backend_repr { - BackendRepr::ScalarPair(_, _) => { + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => { CValue(from.0, dst_layout).load_scalar_pair(fx) } _ => { @@ -607,9 +606,8 @@ impl<'tcx> CPlace<'tcx> { to_ptr.store(fx, val, flags); } CValueInner::ByValPair(val1, val2) => match from.layout().backend_repr { - BackendRepr::ScalarPair(a_scalar, b_scalar) => { - let b_offset = - scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + BackendRepr::ScalarPair { a: _, b: _, b_offset } => { + let b_offset = scalar_pair_convert_b_offset(b_offset); to_ptr.store(fx, val1, flags); to_ptr.offset(fx, b_offset).store(fx, val2, flags); } @@ -627,9 +625,8 @@ impl<'tcx> CPlace<'tcx> { to_ptr.store(fx, val, flags); return; } - BackendRepr::ScalarPair(a_scalar, b_scalar) => { - let b_offset = - scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + BackendRepr::ScalarPair { a: _, b: _, b_offset } => { + let b_offset = scalar_pair_convert_b_offset(b_offset); let (val1, val2) = from.load_scalar_pair(fx); to_ptr.store(fx, val1, flags); to_ptr.offset(fx, b_offset).store(fx, val2, flags); diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index b5d241d8f39f2..3310e4e3a2228 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -56,13 +56,14 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( } } - let (ptr, vtable) = if let BackendRepr::ScalarPair(_, _) = arg.layout().backend_repr { - let (ptr, vtable) = arg.load_scalar_pair(fx); - (Pointer::new(ptr), vtable) - } else { - let (ptr, vtable) = arg.try_to_ptr().unwrap(); - (ptr, vtable.unwrap()) - }; + let (ptr, vtable) = + if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = arg.layout().backend_repr { + let (ptr, vtable) = arg.load_scalar_pair(fx); + (Pointer::new(ptr), vtable) + } else { + let (ptr, vtable) = arg.try_to_ptr().unwrap(); + (ptr, vtable.unwrap()) + }; let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes(); let func_ref = fx.bcx.ins().load( diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f9358872299b7..0bef86b1ae8c1 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1064,9 +1064,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { load }, ) - } else if let abi::BackendRepr::ScalarPair(ref a, ref b) = place.layout.backend_repr { - let b_offset = a.size(self).align_to(b.default_align(self).abi); - + } else if let abi::BackendRepr::ScalarPair { ref a, ref b, b_offset } = + place.layout.backend_repr + { let mut load = |i, scalar: &abi::Scalar, align| { let ptr = if i == 0 { place.val.llval diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 78a4c7e88c895..a5b8068e0f018 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -485,7 +485,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let tp_ty = fn_args.type_at(0); let layout = self.layout_of(tp_ty).layout; let _use_integer_compare = match layout.backend_repr() { - Scalar(_) | ScalarPair(_, _) => true, + Scalar(_) | ScalarPair { a: _, b: _, b_offset: _ } => true, SimdVector { .. } | SimdScalableVector { .. } => false, Memory { .. } => { // For rusty ABIs, small aggregates are actually passed diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 9807a84c0788d..227b513c0ff30 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -75,7 +75,7 @@ fn uncached_gcc_type<'gcc, 'tcx>( }; return cx.context.new_vector_type(element, count); } - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { return cx.type_struct( &[ layout.scalar_pair_element_gcc_type(cx, 0), @@ -182,13 +182,13 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => true, // FIXME(rustc_scalable_vector): Not yet implemented in rustc_codegen_gcc. BackendRepr::SimdScalableVector { .. } => todo!(), - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false, + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => false, } } fn is_gcc_scalar_pair(&self) -> bool { match self.backend_repr { - BackendRepr::ScalarPair(..) => true, + BackendRepr::ScalarPair { .. } => true, BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } @@ -308,8 +308,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { // This must produce the same result for `repr(transparent)` wrappers as for the inner type! // In other words, this should generally not look at the type at all, but only at the // layout. - let (a, b) = match self.backend_repr { - BackendRepr::ScalarPair(ref a, ref b) => (a, b), + let (a, b, b_offset) = match self.backend_repr { + BackendRepr::ScalarPair { ref a, ref b, b_offset } => (a, b, b_offset), _ => bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self), }; let scalar = [a, b][index]; @@ -325,8 +325,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { return cx.type_i1(); } - let offset = - if index == 0 { Size::ZERO } else { a.size(cx).align_to(b.default_align(cx).abi) }; + let offset = if index == 0 { Size::ZERO } else { b_offset }; self.scalar_gcc_type_at(cx, scalar, offset) } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index bdaf72cb17ce6..65bb32ee666f2 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -551,7 +551,9 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Pair(a, b) => { let i = apply(a); let ii = apply(b); - if let BackendRepr::ScalarPair(scalar_a, scalar_b) = arg.layout.backend_repr { + if let BackendRepr::ScalarPair { a: scalar_a, b: scalar_b, b_offset: _ } = + arg.layout.backend_repr + { apply_range_attr(llvm::AttributePlace::Argument(i), scalar_a); let primitive_b = scalar_b.primitive(); let scalar_b = if let rustc_abi::Primitive::Int(int, false) = primitive_b diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 23cf01a84f7fd..e753fff61aa41 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -793,9 +793,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } }); OperandValue::Immediate(llval) - } else if let abi::BackendRepr::ScalarPair(a, b) = place.layout.backend_repr { - let b_offset = a.size(self).align_to(b.default_align(self).abi); - + } else if let abi::BackendRepr::ScalarPair { a, b, b_offset } = place.layout.backend_repr { let mut load = |i, scalar: abi::Scalar, layout, align, offset| { let llptr = if i == 0 { place.val.llval diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 10e152a7c8c63..b0ac5c096efdf 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -118,7 +118,9 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( // If the argument is lowered as a `ScalarPair`, we need to duplicate its activity. // Otherwise, the number of activities won't match the number of LLVM arguments and // this will lead to errors when verifying the Enzyme call. - if let rustc_abi::BackendRepr::ScalarPair(_, _) = layout.backend_repr() { + if let rustc_abi::BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + layout.backend_repr() + { new_activities.push(da[i].clone()); new_positions.push(i + 1 - del_activities); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index bd7a4cfe38d48..54456ddea620b 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -574,7 +574,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let tp_ty = fn_args.type_at(0); let layout = self.layout_of(tp_ty).layout; let use_integer_compare = match layout.backend_repr() { - Scalar(_) | ScalarPair(_, _) => true, + Scalar(_) | ScalarPair { a: _, b: _, b_offset: _ } => true, SimdVector { .. } => false, SimdScalableVector { .. } => { let err = tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 6d0490e4a1f79..d1c2e1d567e40 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -73,7 +73,7 @@ fn uncached_llvm_type<'a, 'tcx>( _ => bug!("`#[rustc_scalable_vector]` tuple struct with too many fields"), }; } - BackendRepr::Memory { .. } | BackendRepr::ScalarPair(..) => {} + BackendRepr::Memory { .. } | BackendRepr::ScalarPair { .. } => {} } let name = match layout.ty.kind() { @@ -228,13 +228,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => true, - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false, + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => false, } } fn is_llvm_scalar_pair(&self) -> bool { match self.backend_repr { - BackendRepr::ScalarPair(..) => true, + BackendRepr::ScalarPair { .. } => true, BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } @@ -313,7 +313,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { return cx.type_i1(); } } - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { // An immediate pair always contains just the two elements, without any padding // filler, as it should never be stored to memory. return cx.type_struct( @@ -346,7 +346,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { // This must produce the same result for `repr(transparent)` wrappers as for the inner type! // In other words, this should generally not look at the type at all, but only at the // layout. - let BackendRepr::ScalarPair(a, b) = self.backend_repr else { + let BackendRepr::ScalarPair { a, b, b_offset: _ } = self.backend_repr else { bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self); }; let scalar = [a, b][index]; diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index b8d1cd3504517..d2583f94cf80c 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -564,7 +564,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( BackendRepr::Scalar(scalar) => { registers_for_primitive(scalar.primitive()); } - BackendRepr::ScalarPair(scalar1, scalar2) => { + BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: _ } => { registers_for_primitive(scalar1.primitive()); registers_for_primitive(scalar2.primitive()); } @@ -641,7 +641,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( } Primitive::Float(_) => bx.inbounds_ptradd(reg_save_area_v, fp_offset_v), }, - BackendRepr::ScalarPair(scalar1, scalar2) => { + BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: offset } => { let ty_lo = bx.cx().scalar_pair_element_backend_type(layout, 0, false); let ty_hi = bx.cx().scalar_pair_element_backend_type(layout, 1, false); @@ -665,9 +665,8 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); - let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); let field0 = tmp; - let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32)); bx.store(reg_lo, field0, align); bx.store(reg_hi, field1, align); @@ -688,9 +687,8 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); - let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); let field0 = tmp; - let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32)); bx.store(reg_lo, field0, align_lo); bx.store(reg_hi, field1, align_hi); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 8c20db5791774..e24e7bad2b945 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1286,9 +1286,24 @@ impl<'a> Linker for EmLinker<'a> { self.cc_arg("-s"); + // Emscripten exposes the program entry point under the JS name `_main` + // regardless of the underlying wasm symbol (which is `__main_argc_argv` + // per the wasm C ABI in the tool-conventions BasicCABI spec), bridging + // the two internally. So the entry symbol must be requested as `_main` + // here rather than as a `_`-prefixed form of its wasm name. + let entry_name = self.sess.target.entry_name.as_ref(); let mut arg = OsString::from("EXPORTED_FUNCTIONS="); let encoded = serde_json::to_string( - &symbols.iter().map(|sym| "_".to_owned() + &sym.name).collect::>(), + &symbols + .iter() + .map(|sym| { + if sym.name == entry_name { + "_main".to_owned() + } else { + "_".to_owned() + &sym.name + } + }) + .collect::>(), ) .unwrap(); debug!("{encoded}"); diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 71315acc4e5db..c586b8080ef30 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -611,7 +611,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // be marked as a `LocalVariable` for MSVC debuggers to visualize // their data correctly. (See #81894 & #88625) let var_ty_layout = self.cx.layout_of(var_ty); - if let BackendRepr::ScalarPair(_, _) = var_ty_layout.backend_repr { + if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + var_ty_layout.backend_repr + { VariableKind::LocalVariable } else { VariableKind::ArgumentVariable(arg_index) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 0b00b22b1a992..9ce8e6e48bbd1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -441,7 +441,7 @@ fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_t signature.push_str(direct_type); } PassMode::Pair(_, _) => match arg_abi.layout.backend_repr { - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { signature.push_str(wasm_primitive(a.primitive(), ptr_type)); signature.push_str(", "); signature.push_str(wasm_primitive(b.primitive(), ptr_type)); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 0246da3f27829..f2594b712d8cd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -168,7 +168,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } ConstValue::ZeroSized => return OperandRef::zero_sized(layout), ConstValue::Slice { alloc_id, meta } => { - let BackendRepr::ScalarPair(a_scalar, _) = layout.backend_repr else { + let BackendRepr::ScalarPair { a: a_scalar, b: _, b_offset: _ } = + layout.backend_repr + else { bug!("from_const: invalid ScalarPair layout: {:#?}", layout); }; let a = Scalar::from_pointer(Pointer::new(alloc_id.into(), Size::ZERO), &bx.tcx()); @@ -222,12 +224,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let val = read_scalar(offset, size, s, bx.immediate_backend_type(layout)); OperandRef { val: OperandValue::Immediate(val), layout, move_annotation: None } } - BackendRepr::ScalarPair( - a @ abi::Scalar::Initialized { .. }, - b @ abi::Scalar::Initialized { .. }, - ) => { + BackendRepr::ScalarPair { + a: a @ abi::Scalar::Initialized { .. }, + b: b @ abi::Scalar::Initialized { .. }, + b_offset, + } => { let (a_size, b_size) = (a.size(bx), b.size(bx)); - let b_offset = (offset + a_size).align_to(b.default_align(bx).abi); assert!(b_offset.bytes() > 0); let a_val = read_scalar( offset, @@ -345,7 +347,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { llval: V, layout: TyAndLayout<'tcx>, ) -> Self { - let val = if let BackendRepr::ScalarPair(..) = layout.backend_repr { + let val = if let BackendRepr::ScalarPair { .. } = layout.backend_repr { debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", llval, layout); // Deconstruct the immediate aggregate. @@ -383,12 +385,15 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } else { let (in_scalar, imm) = match (self.val, self.layout.backend_repr) { // Extract a scalar component from a pair. - (OperandValue::Pair(a_llval, b_llval), BackendRepr::ScalarPair(a, b)) => { + ( + OperandValue::Pair(a_llval, b_llval), + BackendRepr::ScalarPair { a, b, b_offset }, + ) => { if offset.bytes() == 0 { assert_eq!(field.size, a.size(bx.cx())); (Some(a), a_llval) } else { - assert_eq!(offset, a.size(bx.cx()).align_to(b.default_align(bx.cx()).abi)); + assert_eq!(offset, b_offset); assert_eq!(field.size, b.size(bx.cx())); (Some(b), b_llval) } @@ -419,7 +424,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { imm } } - BackendRepr::ScalarPair(_, _) + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } | BackendRepr::Memory { .. } | BackendRepr::SimdScalableVector { .. } => bug!(), }) @@ -705,7 +710,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { let val = match layout.backend_repr { BackendRepr::Memory { .. } if layout.is_zst() => OperandValueBuilder::ZeroSized, BackendRepr::Scalar(s) => OperandValueBuilder::Immediate(Either::Right(s)), - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { OperandValueBuilder::Pair(Either::Right(a), Either::Right(b)) } BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => { @@ -733,7 +738,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { (OperandValue::Immediate(v), BackendRepr::SimdVector { .. }) => { OperandValueBuilder::Vector(Either::Left(v)) } - (OperandValue::Pair(a, b), BackendRepr::ScalarPair(_, _)) => { + (OperandValue::Pair(a, b), BackendRepr::ScalarPair { a: _, b: _, b_offset: _ }) => { OperandValueBuilder::Pair(Either::Left(a), Either::Left(b)) } (_, BackendRepr::Memory { .. }) => { @@ -810,17 +815,18 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") } }, - (OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => { - match &mut self.val { - OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { - update(fst, a, from_sa); - update(snd, b, from_sb); - } - _ => bug!( - "Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}" - ), + ( + OperandValue::Pair(a, b), + BackendRepr::ScalarPair { a: from_sa, b: from_sb, b_offset: _ }, + ) => match &mut self.val { + OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { + update(fst, a, from_sa); + update(snd, b, from_sb); } - } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } + }, (OperandValue::Ref(place), BackendRepr::Memory { .. }) => match &mut self.val { OperandValueBuilder::Vector(val @ Either::Right(())) => { let ibty = bx.cx().immediate_backend_type(self.layout); @@ -1008,10 +1014,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { bx.store_with_flags(val, dest.val.llval, dest.val.align, flags); } OperandValue::Pair(a, b) => { - let BackendRepr::ScalarPair(a_scalar, b_scalar) = dest.layout.backend_repr else { + let BackendRepr::ScalarPair { a: _, b: _, b_offset } = dest.layout.backend_repr + else { bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout); }; - let b_offset = a_scalar.size(bx).align_to(b_scalar.default_align(bx).abi); let val = bx.from_immediate(a); let align = dest.val.align; diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 49f03fe1376e2..f417dfba746f1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -36,7 +36,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // semantics regarding when assignment operators allow overlap of LHS and RHS. if matches!( cg_operand.layout.backend_repr, - BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..), + BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }, ) { debug_assert!(!matches!(cg_operand.val, OperandValue::Ref(..))); } @@ -323,9 +323,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } ( OperandValue::Pair(imm_a, imm_b), - abi::BackendRepr::ScalarPair(in_a, in_b), - abi::BackendRepr::ScalarPair(out_a, out_b), - ) if in_a.size(cx) == out_a.size(cx) && in_b.size(cx) == out_b.size(cx) => { + abi::BackendRepr::ScalarPair { a: in_a, b: in_b, b_offset: in_offset }, + abi::BackendRepr::ScalarPair { a: out_a, b: out_b, b_offset: out_offset }, + ) if in_a.size(cx) == out_a.size(cx) + && in_b.size(cx) == out_b.size(cx) + && in_offset == out_offset => + { OperandValue::Pair( transmute_scalar(bx, imm_a, in_a, out_a), transmute_scalar(bx, imm_b, in_b, out_b), diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 1b6c948657e0d..7295df0ab2210 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -136,7 +136,7 @@ fn const_to_valtree_inner<'tcx>( let val = ecx.read_immediate(place).report_err()?; // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. - if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) { + if matches!(val.layout.backend_repr, BackendRepr::ScalarPair { .. }) { Err(ValTreeCreationError::NonSupportedType(ty)) } else { let val = val.to_scalar(); diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 1f67b896fc8ec..0c1e171c0edc9 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -84,7 +84,7 @@ impl Immediate { pub fn to_scalar(self) -> Scalar { match self { Immediate::Scalar(val) => val, - Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"), + Immediate::ScalarPair { .. } => bug!("Got a scalar pair where a scalar was expected"), Immediate::Uninit => bug!("Got uninit where a scalar was expected"), } } @@ -129,7 +129,10 @@ impl Immediate { ); } } - (Immediate::ScalarPair(a_val, b_val), BackendRepr::ScalarPair(a, b)) => { + ( + Immediate::ScalarPair(a_val, b_val), + BackendRepr::ScalarPair { a, b, b_offset: _ }, + ) => { assert_eq!( a_val.size(), a.size(cx), @@ -263,7 +266,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar_pair(a: Scalar, b: Scalar, layout: TyAndLayout<'tcx>) -> Self { debug_assert!( - matches!(layout.backend_repr, BackendRepr::ScalarPair(..)), + matches!(layout.backend_repr, BackendRepr::ScalarPair { .. }), "`ImmTy::from_scalar_pair` on non-scalar-pair layout" ); let imm = Immediate::ScalarPair(a, b); @@ -276,7 +279,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { debug_assert!( match (imm, layout.backend_repr) { (Immediate::Scalar(..), BackendRepr::Scalar(..)) => true, - (Immediate::ScalarPair(..), BackendRepr::ScalarPair(..)) => true, + (Immediate::ScalarPair { .. }, BackendRepr::ScalarPair { .. }) => true, (Immediate::Uninit, _) if layout.is_sized() => true, _ => false, }, @@ -415,14 +418,15 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { **self } // extract fields from types with `ScalarPair` ABI - (Immediate::ScalarPair(a_val, b_val), BackendRepr::ScalarPair(a, b)) => { - Immediate::from(if offset.bytes() == 0 { - a_val - } else { - assert_eq!(offset, a.size(cx).align_to(b.default_align(cx).abi)); - b_val - }) - } + ( + Immediate::ScalarPair(a_val, b_val), + BackendRepr::ScalarPair { a: _, b: _, b_offset }, + ) => Immediate::from(if offset.bytes() == 0 { + a_val + } else { + assert_eq!(offset, b_offset); + b_val + }), // everything else is a bug _ => bug!( "invalid field access on immediate {} at offset {}, original layout {:#?}", @@ -606,15 +610,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { )?; Some(ImmTy::from_scalar(scalar, mplace.layout)) } - BackendRepr::ScalarPair( - abi::Scalar::Initialized { value: a, .. }, - abi::Scalar::Initialized { value: b, .. }, - ) => { + BackendRepr::ScalarPair { + a: abi::Scalar::Initialized { value: a, .. }, + b: abi::Scalar::Initialized { value: b, .. }, + b_offset, + } => { // We checked `ptr_align` above, so all fields will have the alignment they need. // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. let (a_size, b_size) = (a.size(self), b.size(self)); - let b_offset = a_size.align_to(b.default_align(self).abi); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields let a_val = alloc.read_scalar( alloc_range(Size::ZERO, a_size), @@ -668,10 +672,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !matches!( op.layout().backend_repr, BackendRepr::Scalar(abi::Scalar::Initialized { .. }) - | BackendRepr::ScalarPair( - abi::Scalar::Initialized { .. }, - abi::Scalar::Initialized { .. } - ) + | BackendRepr::ScalarPair { + a: abi::Scalar::Initialized { .. }, + b: abi::Scalar::Initialized { .. }, + b_offset: _, + } ) { span_bug!(self.cur_span(), "primitive read not possible for type: {}", op.layout().ty); } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index d6d73b2d8da81..0c786c5831676 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -713,7 +713,6 @@ where // to handle padding properly, which is only correct if we never look at this data with the // wrong type. - let tcx = *self.tcx; let will_later_validate = M::enforce_validity(self, layout); let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else { // zero-sized access @@ -725,7 +724,7 @@ where alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?; } Immediate::ScalarPair(a_val, b_val) => { - let BackendRepr::ScalarPair(_a, b) = layout.backend_repr else { + let BackendRepr::ScalarPair { a: _, b: _, b_offset } = layout.backend_repr else { span_bug!( self.cur_span(), "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", @@ -733,7 +732,7 @@ where ) }; let a_size = a_val.size(); - let b_offset = a_size.align_to(b.default_align(&tcx).abi); + let b_size = b_val.size(); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, @@ -744,12 +743,12 @@ where // destination now to ensure that no stray pointer fragments are being // preserved (see ). // We can skip this if there is no padding (e.g. for wide pointers). - if !will_later_validate && a_size + b_val.size() != layout.size { + if !will_later_validate && a_size + b_size != layout.size { alloc.write_uninit_full(); } alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?; - alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; + alloc.write_scalar(alloc_range(b_offset, b_size), b_val)?; } Immediate::Uninit => alloc.write_uninit_full(), } @@ -902,17 +901,19 @@ where // padding in the target independent of layout choices. let src_has_padding = match src.layout().backend_repr { BackendRepr::Scalar(_) => false, - BackendRepr::ScalarPair(left, right) + BackendRepr::ScalarPair { a: left, b: right, b_offset: _ } if matches!(src.layout().ty.kind(), ty::Ref(..) | ty::RawPtr(..)) => { // Wide pointers never have padding, so we can avoid calling `size()`. debug_assert_eq!(left.size(self) + right.size(self), src.layout().size); false } - BackendRepr::ScalarPair(left, right) => { + BackendRepr::ScalarPair { a: left, b: right, b_offset: _ } => { let left_size = left.size(self); let right_size = right.size(self); // We have padding if the sizes don't add up to the total. + // (Why don't we need to check the offset? The scalars don't overlap so no padding + // implies `b_offset == left_size`, which would be superfluous to check explicitly.) left_size + right_size != src.layout().size } // Everything else can only exist in memory anyway, so it doesn't matter. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 262ef6ba74ed0..2d412e1124e5a 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1397,7 +1397,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path, Uninit { expected } ), - Immediate::Scalar(..) | Immediate::ScalarPair(..) => + Immediate::Scalar(..) | Immediate::ScalarPair { .. } => bug!("arrays/slices can never have Scalar/ScalarPair layout"), } }; @@ -1486,7 +1486,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.visit_scalar(scalar, scalar_layout)?; } } - BackendRepr::ScalarPair(a_layout, b_layout) => { + BackendRepr::ScalarPair { a: a_layout, b: b_layout, b_offset: _ } => { // We can only proceed if *both* scalars need to be initialized. // FIXME: find a way to also check ScalarPair when one side can be uninit but // the other must be init. @@ -1545,7 +1545,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, .expect("the above checks should have fully handled this situation"); } } - BackendRepr::ScalarPair(a_layout, b_layout) => { + BackendRepr::ScalarPair { a: a_layout, b: b_layout, b_offset: _ } => { // We can only proceed if *both* scalars need to be initialized. // FIXME: find a way to also check ScalarPair when one side can be uninit but // the other must be init. diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 00835a3cc990a..64688f56fe763 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -121,7 +121,7 @@ fn check_validity_requirement_lax<'tcx>( let valid = !this.is_uninhabited() // definitely UB if uninhabited && match this.backend_repr { BackendRepr::Scalar(s) => scalar_allows_raw_init(s), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2) } BackendRepr::SimdVector { element: s, count } => count == 0 || scalar_allows_raw_init(s), diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 8bded7662f423..641c50b620ff5 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -696,11 +696,15 @@ impl TtParser { } fn ambiguity_error<'matcher, T: Tracker<'matcher>>( - &self, + &mut self, parser: &Parser<'_>, matcher: &'matcher [MatcherLoc], track: &mut T, ) -> NamedParseResult { + // Use a reasonable and deterministic ordering for data in the error message. + self.bb_mps.sort_unstable_by_key(|mp| mp.idx); + self.next_mps.sort_unstable_by_key(|mp| mp.idx); + let bb_locs = self.bb_mps.iter().map(|mp| &matcher[mp.idx]); let next_locs = self.next_mps.iter().map(|mp| &matcher[mp.idx]); track.ambiguity(parser, bb_locs, next_locs); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 383023aa24457..ea0f8f22ab187 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1325,7 +1325,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // FIXME(generic_const_exprs): Revisit this before stabilization. // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. let ty = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(); - if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) + if ty.has_type_flags(ty::TypeFlags::HAS_CONST_ALIAS) && cx.tcx.features().generic_const_exprs() { return; @@ -2438,7 +2438,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // Check if this ADT has a constrained layout (like `NonNull` and friends). if let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(ty)) { - if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair { a: scalar, .. } = &layout.backend_repr { let range = scalar.valid_range(cx); diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 43ed22927da56..f0692817fb60b 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -44,7 +44,7 @@ impl<'tcx> TyCtxt<'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { + if ty.has_type_flags(ty::TypeFlags::HAS_CONST_ALIAS) { ty.super_fold_with(self) } else { ty diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 9d0fcc50ef224..a58e517e05f61 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -76,7 +76,7 @@ impl OffloadMetadata { Ty<'tcx>: TyAbiInterface<'tcx, C>, { match arg_abi.layout.backend_repr { - BackendRepr::ScalarPair(_, _) => (0..2) + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => (0..2) .map(|i| { let ty = arg_abi.layout.field(cx, i).ty; (OffloadMetadata::from_ty(tcx, ty), ty) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index c7d18bd1cc92f..7e128ad72454e 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -647,7 +647,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // a pair and sometimes not. But as a hack we always return a pair // and just make the 2nd component `Bottom` when it does not exist. Some(val) => { - if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) { + if matches!(val.layout.backend_repr, BackendRepr::ScalarPair { .. }) { let (val, overflow) = val.to_scalar_pair(); (FlatSet::Elem(val), FlatSet::Elem(overflow)) } else { @@ -814,7 +814,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { return Some(Const::Val(ConstValue::Scalar(value), ty)); } - if matches!(layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) { + if matches!(layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }) { let alloc_id = ecx .intern_with_temp_alloc(layout, |ecx, dest| { try_write_constant(ecx, dest, place, ty, state, map) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 2db4502ecc6b6..f00dd3fde5aac 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -608,7 +608,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let fields = fields.iter().map(|&f| self.eval_to_const(f)).collect::>>()?; let variant = if ty.ty.is_enum() { Some(variant) } else { None }; - let (BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) = ty.backend_repr + let (BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }) = ty.backend_repr else { return None; }; @@ -639,7 +639,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { ImmTy::from_immediate(Immediate::Uninit, ty).into() } else if matches!( ty.backend_repr, - BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..) + 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()?; @@ -738,11 +738,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { s1.size(&self.ecx) == s2.size(&self.ecx) && !matches!(s1.primitive(), Primitive::Pointer(..)) } - (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => { + ( + BackendRepr::ScalarPair { a: a1, b: b1, b_offset: b1_offset }, + BackendRepr::ScalarPair { a: a2, b: b2, b_offset: b2_offset }, + ) => { a1.size(&self.ecx) == a2.size(&self.ecx) && b1.size(&self.ecx) == b2.size(&self.ecx) - // The alignment of the second component determines its offset, so that also needs to match. - && b1.default_align(&self.ecx) == b2.default_align(&self.ecx) + // The first component is always at offset zero, but the offset to the second + // component needs to match as well for us to be able to transmute. + && b1_offset == b2_offset // None of the inputs may be a pointer. && !matches!(a1.primitive(), Primitive::Pointer(..)) && !matches!(b1.primitive(), Primitive::Pointer(..)) @@ -1804,7 +1808,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { true } } - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { + // The offset is irrelevant to niches since it can only cause padding, + // which can never have a niche since it's uninitialized. !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx) } BackendRepr::SimdVector { .. } @@ -1902,7 +1908,10 @@ fn op_to_prop_const<'tcx>( // But we *do* want to synthesize any size constant if it is entirely uninit because that // benefits codegen, which has special handling for them. if !op.is_immediate_uninit() - && !matches!(op.layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) + && !matches!( + op.layout.backend_repr, + BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. } + ) { return None; } diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index cc216c39b8599..cf3e22212f95a 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -45,7 +45,7 @@ pub(crate) fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool // Only consider global clauses to simplify. TypeFlags::HAS_FREE_LOCAL_NAMES // Clauses that refer to alias constants as they cause cycles. - | TypeFlags::HAS_CT_PROJECTION, + | TypeFlags::HAS_CONST_ALIAS, ) }); let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index cd8dc963eb33c..e762821724231 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -563,7 +563,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; let val = self.use_ecx(|this| this.ecx.binary_op(bin_op, &left, &right))?; - if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) { + if matches!(val.layout.backend_repr, BackendRepr::ScalarPair { .. }) { // FIXME `Value` should properly support pairs in `Immediate`... but currently // it does not. let (val, overflow) = val.to_pair(&self.ecx); @@ -629,7 +629,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // so bail out if the target is not one. match (value.layout.backend_repr, to.backend_repr) { (BackendRepr::Scalar(..), BackendRepr::Scalar(..)) => {} - (BackendRepr::ScalarPair(..), BackendRepr::ScalarPair(..)) => {} + (BackendRepr::ScalarPair { .. }, BackendRepr::ScalarPair { .. }) => {} _ => return None, } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 98b2ed1642582..201601d91adfa 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -3424,6 +3424,7 @@ impl<'a> Parser<'a> { let (pat, colon) = this.parse_fn_param_pat_colon()?; if !colon { let mut err = this.unexpected().unwrap_err(); + let pat_span = pat.span; return if let Some(ident) = this.parameter_without_type( &mut err, pat, @@ -3432,7 +3433,9 @@ impl<'a> Parser<'a> { fn_parse_mode, ) { let guar = err.emit(); - Ok((dummy_arg(ident, guar), Trailing::No, UsePreAttrPos::No)) + let mut arg = dummy_arg(ident, guar); + arg.span = pat_span; + Ok((arg, Trailing::No, UsePreAttrPos::No)) } else { Err(err) }; diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index cde885def8a41..02674e4107c77 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -241,7 +241,11 @@ pub struct NumScalableVectors(pub(crate) u8); #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum ValueAbi { Scalar(Scalar), - ScalarPair(Scalar, Scalar), + ScalarPair { + a: Scalar, + b: Scalar, + b_offset: Size, + }, Vector { element: Scalar, count: u64, @@ -262,7 +266,7 @@ impl ValueAbi { pub fn is_unsized(&self) -> bool { match *self { ValueAbi::Scalar(_) - | ValueAbi::ScalarPair(..) + | ValueAbi::ScalarPair { .. } | ValueAbi::Vector { .. } // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 7e0b04f8a7f61..bbc7435a6c596 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -271,8 +271,12 @@ impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { ) -> Self::T { match *self { rustc_abi::BackendRepr::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables, cx)), - rustc_abi::BackendRepr::ScalarPair(first, second) => { - ValueAbi::ScalarPair(first.stable(tables, cx), second.stable(tables, cx)) + rustc_abi::BackendRepr::ScalarPair { a: first, b: second, b_offset: second_offset } => { + ValueAbi::ScalarPair { + a: first.stable(tables, cx), + b: second.stable(tables, cx), + b_offset: second_offset.stable(tables, cx), + } } rustc_abi::BackendRepr::SimdVector { element, count } => { ValueAbi::Vector { element: element.stable(tables, cx), count } diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index ce69427cbdd59..ec2c30756ddc0 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -56,7 +56,7 @@ fn softfloat_float_abi(target: &Target, arg: &mut ArgAbi<'_, Ty>) { && let Primitive::Float(f) = s.primitive() { arg.cast_to(Reg { kind: RegKind::Integer, size: f.size() }); - } else if let BackendRepr::ScalarPair(s1, s2) = arg.layout.backend_repr + } else if let BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } = arg.layout.backend_repr && (matches!(s1.primitive(), Primitive::Float(_)) || matches!(s2.primitive(), Primitive::Float(_))) { diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 6d3826abf27a8..3e9ff2b672a07 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -89,7 +89,7 @@ where return Err(CannotUseFpConv); } BackendRepr::SimdScalableVector { .. } => panic!("scalable vectors are unsupported"), - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 4bc88cc4f9705..54f4ff77627b9 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -390,17 +390,15 @@ impl<'a, Ty: fmt::Display> fmt::Debug for ArgAbi<'a, Ty> { impl<'a, Ty> ArgAbi<'a, Ty> { /// This defines the "default ABI" for that type, that is then later adjusted in `fn_abi_adjust_for_abi`. pub fn new( - cx: &impl HasDataLayout, layout: TyAndLayout<'a, Ty>, scalar_attrs: impl Fn(Scalar, Size) -> ArgAttributes, ) -> Self { let mode = match layout.backend_repr { _ if layout.is_zst() => PassMode::Ignore, BackendRepr::Scalar(scalar) => PassMode::Direct(scalar_attrs(scalar, Size::ZERO)), - BackendRepr::ScalarPair(a, b) => PassMode::Pair( - scalar_attrs(a, Size::ZERO), - scalar_attrs(b, a.size(cx).align_to(b.default_align(cx).abi)), - ), + BackendRepr::ScalarPair { a, b, b_offset } => { + PassMode::Pair(scalar_attrs(a, Size::ZERO), scalar_attrs(b, b_offset)) + } BackendRepr::SimdVector { .. } => PassMode::Direct(ArgAttributes::new()), BackendRepr::Memory { .. } => Self::indirect_pass_mode(&layout), BackendRepr::SimdScalableVector { .. } => PassMode::Direct(ArgAttributes::new()), @@ -877,7 +875,7 @@ where { match layout.backend_repr { BackendRepr::Scalar(scalar) => !scalar.is_uninit_valid(), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { !s1.is_uninit_valid() && !s2.is_uninit_valid() // Ensure there is no padding. diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index bc81ec95c86e3..e79ce0557ce80 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -94,7 +94,7 @@ where BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => { return Err(CannotUseFpConv); } - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") } diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index b5e5c3e2a88b3..6b19f8ebd76ce 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -67,7 +67,7 @@ fn classify<'a, Ty, C>( }, BackendRepr::SimdVector { .. } => {} BackendRepr::SimdScalableVector { .. } => {} - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") } diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 81ff1a2a45900..a80088e41cd31 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -92,7 +92,7 @@ where Ty: TyAbiInterface<'a, C> + Copy, { match layout.backend_repr { - BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) => false, + BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false, BackendRepr::SimdVector { .. } => true, BackendRepr::Memory { .. } => { for i in 0..layout.fields.count() { @@ -211,7 +211,7 @@ where if !fn_abi.ret.is_ignore() { let has_float = match fn_abi.ret.layout.backend_repr { BackendRepr::Scalar(s) => matches!(s.primitive(), Primitive::Float(_)), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { matches!(s1.primitive(), Primitive::Float(_)) || matches!(s2.primitive(), Primitive::Float(_)) } diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 3055d18ffa014..5ab2834807d67 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -61,7 +61,7 @@ where BackendRepr::SimdScalableVector { .. } => panic!("scalable vectors are unsupported"), - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => { for i in 0..layout.fields.count() { let field_off = off + layout.fields.offset(i); classify(cx, layout.field(cx, i), cls, field_off)?; diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index cece9d032b53a..4aaf34e56ce4b 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -12,7 +12,7 @@ where let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| { match a.layout.backend_repr { BackendRepr::Memory { sized: false } => {} - BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { sized: true } => { match a.layout.size.bits() { 8 => a.cast_to(Reg::i8()), 16 => a.cast_to(Reg::i16()), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index 2104286ec8684..bda2681605814 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -29,6 +29,11 @@ pub(crate) fn target() -> Target { crt_static_default: true, crt_static_allows_dylibs: true, main_needs_argc_argv: true, + // Use the wasm C-ABI entry name from the tool-conventions BasicCABI + // spec rather than a raw `main`, as referenced by emscripten's crt/libc. + // Required for entry paths like `-sPROXY_TO_PTHREAD`, whose + // `crt1_proxy_main` links against `__main_argc_argv`. + entry_name: "__main_argc_argv".into(), panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index e8371a3c0f257..ac6100747970e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -376,7 +376,7 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { // of type/const and we need to continue folding it to reveal the TAIT behind it // or further normalize nested alias consts. if res != term.to_term(tcx, ty::IsRigid::No) - && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) + && (res.has_type_flags(ty::TypeFlags::HAS_CONST_ALIAS) || matches!( term.kind, ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 51db1b8efde2f..8873fb13efe03 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -486,7 +486,7 @@ fn fn_abi_sanity_check<'tcx>( BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => {} - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { panic!("`PassMode::Direct` used for ScalarPair type {}", arg.layout.ty) } BackendRepr::Memory { sized } => { @@ -513,7 +513,7 @@ fn fn_abi_sanity_check<'tcx>( // Similar to `Direct`, we need to make sure that backends use `layout.backend_repr` // and ignore the rest of the layout. assert!( - matches!(arg.layout.backend_repr, BackendRepr::ScalarPair(..)), + matches!(arg.layout.backend_repr, BackendRepr::ScalarPair { .. }), "PassMode::Pair for type {}", arg.layout.ty ); @@ -612,7 +612,7 @@ fn fn_abi_new_uncached<'tcx>( layout }; - Ok(ArgAbi::new(cx, layout, |scalar, offset| { + Ok(ArgAbi::new(layout, |scalar, offset| { arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, determined_fn_def_id) })) }; @@ -741,7 +741,7 @@ fn make_thin_self_ptr<'tcx>( Ty::new_mut_ptr(tcx, layout.ty) } else { match layout.backend_repr { - BackendRepr::ScalarPair(..) | BackendRepr::Scalar(..) => (), + BackendRepr::ScalarPair { .. } | BackendRepr::Scalar(..) => (), _ => bug!("receiver type has unsupported layout: {:?}", layout), } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index be0042f697e5d..0defe2e0ac38f 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -291,7 +291,8 @@ fn layout_of_uncached<'tcx>( } } ty::PatternKind::NotNull => { - if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + if let BackendRepr::Scalar(scalar) + | BackendRepr::ScalarPair { a: scalar, b: _, b_offset: _ } = &mut layout.backend_repr { scalar.valid_range_mut().start = 1; diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index decf1ffb5570d..70e980490f36b 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -158,15 +158,21 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou } } } - BackendRepr::ScalarPair(scalar1, scalar2) => { + BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset } => { // Check that the underlying pair of fields matches. let inner = skip_newtypes(cx, layout); assert!( - matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair(..)), + matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair { .. }), "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}", layout.ty, inner.ty ); + // `a` is at memory offset zero, so to keep them from overlapping the offset + // to `b` must be at least as much as the size of `a`. + assert!( + b_offset >= scalar1.size(cx), + "`ScalarPair` scalars are overlapping in {layout:?}", + ); if matches!(inner.layout.variants(), Variants::Multiple { .. }) { // FIXME: ScalarPair for enums is enormously complicated and it is very hard // to check anything about them. @@ -234,6 +240,10 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou offset2, field2_offset, "`ScalarPair` second field at bad offset in {inner:#?}", ); + assert_eq!( + b_offset, field2_offset, + "`ScalarPair` with inconsistent b_offset in {inner:#?}", + ); assert_eq!( field2.size, size2, "`ScalarPair` second field with bad size in {inner:#?}", @@ -325,8 +335,11 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou }; let abi_coherent = match (layout.backend_repr, variant.backend_repr) { (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2), - (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => { - scalar_coherent(a1, a2) && scalar_coherent(b1, b2) + ( + BackendRepr::ScalarPair { a: a1, b: b1, b_offset: b1_offset }, + BackendRepr::ScalarPair { a: a2, b: b2, b_offset: b2_offset }, + ) => { + scalar_coherent(a1, a2) && scalar_coherent(b1, b2) && b1_offset == b2_offset } (BackendRepr::Memory { .. }, _) => true, _ => false, diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 03f90215ad1aa..214a83a9b491b 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -80,7 +80,7 @@ bitflags::bitflags! { /// Does this have `Inherent`? const HAS_TY_INHERENT = 1 << 13; /// Does this have `ConstKind::Alias`? - const HAS_CT_PROJECTION = 1 << 14; + const HAS_CONST_ALIAS = 1 << 14; /// Does this have `Alias` or `ConstKind::Alias`? /// @@ -89,7 +89,7 @@ bitflags::bitflags! { | TypeFlags::HAS_TY_FREE_ALIAS.bits() | TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_INHERENT.bits() - | TypeFlags::HAS_CT_PROJECTION.bits(); + | TypeFlags::HAS_CONST_ALIAS.bits(); /// Is a type or const error reachable? const HAS_NON_REGION_ERROR = 1 << 15; @@ -476,7 +476,7 @@ impl FlagComputation { ty::ConstKind::Alias(is_rigid, alias_const) => { self.add_is_rigid(is_rigid); self.add_args(alias_const.args.as_slice()); - self.add_flags(TypeFlags::HAS_CT_PROJECTION); + self.add_flags(TypeFlags::HAS_CONST_ALIAS); } ty::ConstKind::Infer(infer) => match infer { ty::InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH), diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 065cc128101ae..44211354f520e 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1430,6 +1430,7 @@ impl VecDeque { /// buf.truncate(1); /// assert_eq!(buf, [5]); /// ``` + #[doc(alias = "retain_front")] #[stable(feature = "deque_extras", since = "1.16.0")] pub fn truncate(&mut self, len: usize) { /// Runs the destructor for all items in the slice when it gets dropped (normally or @@ -1484,7 +1485,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// # #![feature(vec_deque_truncate_front)] /// use std::collections::VecDeque; /// /// let mut buf = VecDeque::new(); @@ -1493,11 +1493,12 @@ impl VecDeque { /// buf.push_front(15); /// assert_eq!(buf, [15, 10, 5]); /// assert_eq!(buf.as_slices(), (&[15, 10, 5][..], &[][..])); - /// buf.truncate_front(1); + /// buf.retain_back(1); /// assert_eq!(buf.as_slices(), (&[5][..], &[][..])); /// ``` - #[unstable(feature = "vec_deque_truncate_front", issue = "140667")] - pub fn truncate_front(&mut self, len: usize) { + #[doc(alias = "truncate_front")] + #[stable(feature = "vec_deque_truncate_front", since = "CURRENT_RUSTC_VERSION")] + pub fn retain_back(&mut self, len: usize) { /// Runs the destructor for all items in the slice when it gets dropped (normally or /// during unwinding). struct Dropper<'a, T>(&'a mut [T]); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index c3b86a635bd0f..7ca6c7b951ffc 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2629,23 +2629,49 @@ impl Vec { self.dedup_by(|a, b| key(a) == key(b)) } - /// Removes all but the first of consecutive elements in the vector satisfying a given equality - /// relation. + /// Removes all but the first of consecutive elements in the vector that are + /// "equal" according to the given predicate function. /// - /// The `same_bucket` function is passed references to two elements from the vector and - /// must determine if the elements compare equal. The elements are passed in opposite order - /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is removed. + /// The predicate `same_bucket(x, p)` is passed references to two elements. + /// If it returns `true`, the element `x` is removed from the vector. /// - /// If the vector is sorted, this removes all duplicates. + /// The element `p` occurs *before* `x` in the vector (`[.., p, .., x, ..]`), + /// so `same_bucket(x, p)` is receiving them in reversed order (unlike [`windows`]). + /// + /// If the vector is sorted, this removes all duplicates. For more complicated predicates + /// however, the order (ascending vs. descending) can matter. + /// + /// [`windows`]: slice::windows /// /// # Examples /// /// ``` /// let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"]; + /// vec.dedup_by(|x, p| x.eq_ignore_ascii_case(p)); + /// assert_eq!(vec, ["foo", "bar", "baz", "bar"]); + /// ``` /// - /// vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); + /// Both references passed to `same_bucket` are mutable. + /// This allows merging elements by mutating `p` and returning `true`: /// - /// assert_eq!(vec, ["foo", "bar", "baz", "bar"]); + /// ``` + /// let mut ranges = vec![1..2, 2..4, 2..5, 8..9]; + /// + /// // Sort ranges by start, and if equal, by end (lexicographically) + /// // Sorting in reverse instead (`x.start.cmp(&p.start)...`) would later fail + /// ranges.sort_unstable_by(|p, x| p.start.cmp(&x.start).then(p.end.cmp(&x.end))); + /// + /// // Merge touching (`1..2` and `2..4`) and then overlapping (`1..4` and `2..5`) ranges + /// ranges.dedup_by(|x, p| { + /// if p.end >= x.start { + /// p.end = p.end.max(x.end); + /// true + /// } else { + /// false + /// } + /// }); + /// + /// assert_eq!(ranges, [1..5, 8..9]); /// ``` #[stable(feature = "dedup_by", since = "1.16.0")] pub fn dedup_by(&mut self, mut same_bucket: F) diff --git a/library/alloctests/Cargo.toml b/library/alloctests/Cargo.toml index 3b522bf80a217..791b8ecbafce3 100644 --- a/library/alloctests/Cargo.toml +++ b/library/alloctests/Cargo.toml @@ -22,6 +22,10 @@ rand_xorshift = "0.4.0" name = "alloctests" path = "tests/lib.rs" +[[test]] +name = "c_str_alloc_error" +path = "tests/c_str_alloc_error.rs" + [[test]] name = "vec_deque_alloc_error" path = "tests/vec_deque_alloc_error.rs" diff --git a/library/alloctests/tests/c_str_alloc_error.rs b/library/alloctests/tests/c_str_alloc_error.rs new file mode 100644 index 0000000000000..669a783645baa --- /dev/null +++ b/library/alloctests/tests/c_str_alloc_error.rs @@ -0,0 +1,72 @@ +//! Regression test for the panic-safety fix in rust-lang/rust#155707. +//! +//! rust-lang/rust#70201 gave `::clone_into` a path that moved the +//! target `CString`'s buffer out before growing a `Vec`; if that growth's allocation +//! failed and unwound, the target was left without its nul terminator. This only +//! reproduces under Miri: in a normal build the `#[global_allocator]` below can't +//! intercept the reallocation inside `CString::clone_into` (it lives in libstd, which +//! library tests link with `-C prefer-dynamic`), so as a regular test it just checks +//! the happy path. + +// Disabled under Miri on Windows: a `#[global_allocator]` wrapping `System` trips +// Stacked Borrows there, and it affects libtest's own allocations, not just this test +// (so `#[ignore]` would not be enough). See . +#![cfg(not(all(miri, windows)))] +#![feature(alloc_error_hook)] + +use std::alloc::{GlobalAlloc, Layout, System, set_alloc_error_hook}; +use std::ffi::CString; +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::sync::atomic::{AtomicBool, Ordering}; + +// Once armed, the first allocation of 8 bytes or more fails and disarms, so the +// reallocation inside `clone_into`'s grow path fails while the runtime's own +// smaller allocations keep succeeding. +struct OneShotFailingAlloc; + +static ARMED: AtomicBool = AtomicBool::new(false); + +unsafe impl GlobalAlloc for OneShotFailingAlloc { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.size() >= 8 && ARMED.swap(false, Ordering::SeqCst) { + return core::ptr::null_mut(); + } + unsafe { System.alloc(layout) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe { System.dealloc(ptr, layout) } + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if new_size >= 8 && ARMED.swap(false, Ordering::SeqCst) { + return core::ptr::null_mut(); + } + unsafe { System.realloc(ptr, layout, new_size) } + } +} + +#[global_allocator] +static ALLOC: OneShotFailingAlloc = OneShotFailingAlloc; + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn clone_into_alloc_failure_leaves_target_valid() { + set_alloc_error_hook(|_| panic!("alloc error")); + + let src = CString::new("a fairly long value").unwrap(); + let mut target = CString::new("x").unwrap(); + + ARMED.store(true, Ordering::SeqCst); + // Under Miri the failing allocator is honored, so this reallocation unwinds; in a + // normal build the allocator can't intercept it and `clone_into` just succeeds. + let res = catch_unwind(AssertUnwindSafe(|| src.as_c_str().clone_into(&mut target))); + ARMED.store(false, Ordering::SeqCst); + + if cfg!(miri) { + assert!(res.is_err(), "clone_into should have unwound on the alloc failure"); + } + // Either way `target` must still end in its nul terminator. Before the fix the Miri + // unwind left it empty (also caught as a bad write in `CString`'s destructor). + assert_eq!(target.as_bytes_with_nul().last(), Some(&0)); +} diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f89b974cd290c..2ede9730d12dc 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -36,7 +36,6 @@ #![feature(str_as_str)] #![feature(strict_provenance_lints)] #![feature(string_replace_in_place)] -#![feature(vec_deque_truncate_front)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![feature(vec_peek_mut)] diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index 91843dfd00585..2e75b7b07e63f 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -1635,7 +1635,7 @@ fn truncate_leak() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn truncate_front_leak() { +fn retain_back_leak() { struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); let mut q = VecDeque::new(); @@ -1648,7 +1648,7 @@ fn truncate_front_leak() { q.push_front(D(false)); q.push_front(D(false)); - catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok(); + catch_unwind(AssertUnwindSafe(|| q.retain_back(1))).ok(); assert_eq!(DROPS.get(), 7); } @@ -1817,20 +1817,20 @@ fn test_collect_from_into_iter_keeps_allocation() { } #[test] -fn test_truncate_front() { +fn test_retain_back() { let mut v = VecDeque::with_capacity(13); v.extend(0..7); assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); - v.truncate_front(10); + v.retain_back(10); assert_eq!(v.len(), 7); assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); - v.truncate_front(7); + v.retain_back(7); assert_eq!(v.len(), 7); assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); - v.truncate_front(3); + v.retain_back(3); assert_eq!(v.as_slices(), ([4, 5, 6].as_slice(), [].as_slice())); assert_eq!(v.len(), 3); - v.truncate_front(0); + v.retain_back(0); assert_eq!(v.as_slices(), ([].as_slice(), [].as_slice())); assert_eq!(v.len(), 0); @@ -1841,13 +1841,13 @@ fn test_truncate_front() { v.push_front(8); v.push_front(7); assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(12); + v.retain_back(12); assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(10); + v.retain_back(10); assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(8); + v.retain_back(8); assert_eq!(v.as_slices(), ([9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(5); + v.retain_back(5); assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice())); } @@ -1855,7 +1855,7 @@ fn test_truncate_front() { fn test_extend_from_within() { let mut v = VecDeque::with_capacity(8); v.extend(0..6); - v.truncate_front(4); + v.retain_back(4); assert_eq!(v, [2, 3, 4, 5]); v.extend_from_within(1..4); assert_eq!(v, [2, 3, 4, 5, 3, 4, 5]); @@ -1915,7 +1915,7 @@ fn test_extend_from_within_clone() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); v.extend_from_within(2..); @@ -1947,7 +1947,7 @@ fn test_extend_from_within_clone_panic() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); // panic after wrapping @@ -1963,7 +1963,7 @@ fn test_extend_from_within_clone_panic() { // nothing should have been dropped assert_eq!(drop_count.get(), 0); - v.truncate_front(2); + v.retain_back(2); assert_eq!(drop_count.get(), 4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1]); @@ -1985,7 +1985,7 @@ fn test_extend_from_within_clone_panic() { fn test_prepend_from_within() { let mut v = VecDeque::with_capacity(8); v.extend(0..6); - v.truncate_front(4); + v.retain_back(4); v.prepend_from_within(..=0); assert_eq!(v.as_slices(), ([2, 2, 3, 4, 5].as_slice(), [].as_slice())); v.prepend_from_within(2..); @@ -2007,7 +2007,7 @@ fn test_prepend_from_within_clone() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); v.prepend_from_within(..2); @@ -2034,7 +2034,7 @@ fn test_prepend_from_within_clone_panic() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); // panic after wrapping @@ -2050,7 +2050,7 @@ fn test_prepend_from_within_clone_panic() { // nothing should have been dropped assert_eq!(drop_count.get(), 0); - v.truncate_front(2); + v.retain_back(2); assert_eq!(drop_count.get(), 4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [2, 3]); @@ -2071,7 +2071,7 @@ fn test_prepend_from_within_clone_panic() { #[test] fn test_extend_and_prepend_from_within() { let mut v = ('0'..='9').map(String::from).collect::>(); - v.truncate_front(5); + v.retain_back(5); v.extend_from_within(4..); v.prepend_from_within(..2); assert_eq!(v.iter().map(|s| &**s).collect::(), "56567899"); @@ -2095,7 +2095,7 @@ fn test_extend_front() { let mut v = VecDeque::with_capacity(8); let cap = v.capacity(); v.extend(0..4); - v.truncate_front(2); + v.retain_back(2); v.extend_front(4..8); assert_eq!(v.as_slices(), ([7, 6].as_slice(), [5, 4, 2, 3].as_slice())); assert_eq!(v.capacity(), cap); diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index e97398aa5dc4a..44a8ab6e54196 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -1,4 +1,7 @@ +use super::{AllocError, GlobalAllocator}; use crate::alloc::Layout; +use crate::hint::assert_unchecked; +use crate::ptr::NonNull; use crate::{cmp, ptr}; /// A memory allocator that can be registered as the standard library’s default @@ -301,3 +304,72 @@ pub unsafe trait GlobalAlloc { new_ptr } } + +/// Allows all [`GlobalAllocator`]s to be used with the legacy [`GlobalAlloc`] interface. +#[stable(feature = "global_alloc", since = "1.28.0")] +unsafe impl GlobalAlloc for A +where + A: GlobalAllocator + ?Sized, +{ + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: guaranteed by the caller. + // This might lead to the removal of zero-size checks inside the + // `Allocator` implementation. + unsafe { assert_unchecked(layout.size() != 0) }; + match self.allocate(layout) { + Ok(ptr) => ptr.cast().as_ptr(), + Err(AllocError) => ptr::null_mut(), + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(layout.size() != 0) }; + // SAFETY: only non-null pointers can be currently allocated. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + // SAFETY: guaranteed by caller. + unsafe { self.deallocate(ptr, layout) }; + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(layout.size() != 0) }; + match self.allocate_zeroed(layout) { + Ok(ptr) => ptr.cast().as_ptr(), + Err(AllocError) => ptr::null_mut(), + } + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(layout.size() != 0) }; + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(new_size != 0) }; + + // SAFETY: only non-null pointers can be currently allocated. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + 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: + // Two preconditions are guaranteed by the caller: + // * `ptr` is currently allocated with this allocator. + // * `layout` fits the block of memory. + // The size precondition is upheld by selecting between `grow` and `shrink` + // based on the size. + let ptr = unsafe { + if new_size >= layout.size() { + self.grow(ptr, layout, new_layout) + } else { + self.shrink(ptr, layout, new_layout) + } + }; + + match ptr { + Ok(ptr) => ptr.cast().as_ptr(), + Err(AllocError) => ptr::null_mut(), + } + } +} diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 806f67728efb8..339bc310c0b61 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -447,6 +447,96 @@ pub const unsafe trait Allocator { } } +/// An [`Allocator`] that can be registered as the standard library’s default +/// through the `#[global_allocator]` attribute. +/// +/// Types implementing this trait can be used as the default allocator for +/// memory allocations through `Box`, `Vec` and the collection types. For +/// instance, the `System` allocator implements this trait, and thus can be +/// explicitly set as the default like so: +/// ``` +/// use std::alloc::System; +/// +/// #[global_allocator] +/// static ALLOCATOR: System = System; +/// ``` +/// +/// The `Global` allocator forwards all memory allocation requests to the +/// `static` annotated with `#[global_allocator]`. Hence, `Global` does not +/// implement `GlobalAllocator` itself, as that would lead to infinite recursion. +/// +/// # Note to implementors +/// +/// This trait is used to prevent the infinite recursion that would occur if the +/// default allocator were to attempt to allocate memory through `Global` (and +/// thus from itself). +/// +/// When to implement this trait: +/// * for custom global allocators that only use system memory allocation +/// services. +/// * for allocators that wrap another allocator that implements `GlobalAllocator`. +/// +/// When **not** to implement this trait: +/// * for wrappers of arbitrary allocators (which might end up being `Global`, +/// leading to infinite recursion). +/// +/// # Safety +/// +/// In addition to the safety requirements of `Allocator`, global allocators are +/// subject to some additional constraints: +/// +/// * It's undefined behavior if global allocators unwind. This restriction may +/// be lifted in the future, but currently a panic from any of these +/// functions may lead to memory unsafety. +/// +/// * You must not rely on allocations actually happening, even if there are explicit +/// heap allocations in the source. The optimizer may detect unused allocations that it can either +/// eliminate entirely or move to the stack and thus never invoke the allocator. The +/// optimizer may further assume that allocation is infallible, so code that used to fail due +/// to allocator failures may now suddenly work because the optimizer worked around the +/// need for an allocation. More concretely, the following code example is unsound, irrespective +/// of whether your custom allocator allows counting how many allocations have happened. +/// +/// ```rust,ignore (unsound and has placeholders) +/// drop(Box::new(42)); +/// let number_of_heap_allocs = /* call private allocator API */; +/// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } +/// ``` +/// +/// Note that the optimizations mentioned above are not the only +/// optimization that can be applied. You may generally not rely on heap allocations +/// happening if they can be removed without changing program behavior. +/// Whether allocations happen or not is not part of the program behavior, even if it +/// could be detected via an allocator that tracks allocations by printing or otherwise +/// having side effects. +/// +/// # Re-entrance +/// +/// When implementing a global allocator, one has to be careful not to create an infinitely recursive +/// implementation by accident, as many constructs in the Rust standard library may allocate in +/// their implementation. For example, on some platforms, [`std::sync::Mutex`] may allocate, so using +/// it is highly problematic in a global allocator. +/// +/// For this reason, one should generally stick to library features available through +/// [`core`], and avoid using [`std`] in a global allocator. A few features from [`std`] are +/// guaranteed to not use `#[global_allocator]` to allocate: +/// +/// - [`std::thread_local`], +/// - [`std::thread::current`], +/// - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and +/// [`Clone`] implementation. +/// +/// [`std`]: ../../std/index.html +/// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html +/// [`std::thread_local`]: ../../std/macro.thread_local.html +/// [`std::thread::current`]: ../../std/thread/fn.current.html +/// [`std::thread::park`]: ../../std/thread/fn.park.html +/// [`std::thread::Thread`]: ../../std/thread/struct.Thread.html +/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark +#[unstable(feature = "allocator_api", issue = "32838")] +#[expect(multiple_supertrait_upcastable)] +pub unsafe trait GlobalAllocator: Allocator + Sync + 'static {} + #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] const unsafe impl Allocator for &A diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 3371b2cecbd79..328e9b47966c9 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -248,14 +248,26 @@ use crate::ops::ControlFlow; #[rustc_diagnostic_item = "PartialEq"] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] pub const trait PartialEq: PointeeSized { - /// Tests for `self` and `other` values to be equal, and is used by `==`. + /// Equality operator `==`. + /// + /// Implementation of the "is equal to" operator `==`: + /// tests whether its arguments are equal. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "cmp_partialeq_eq"] fn eq(&self, other: &Rhs) -> bool; - /// Tests for `!=`. The default implementation is almost always sufficient, - /// and should not be overridden without very good reason. + /// Inequality operator `!=`. + /// + /// Implementation of the "is not equal to" or "is different from" operator `!=`: + /// tests whether its arguments are different. + /// + /// # Default implementation + /// The default implementation of the inequality operator simply calls + /// the implementation of the equality operator and negates the result. + /// + /// This default shouldn't be overridden without good reason, + /// such as when forwarding to another PartialEq implementation. #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1869,19 +1881,31 @@ mod impls { use crate::ops::ControlFlow::{self, Break, Continue}; use crate::panic::const_assert; - macro_rules! partial_eq_impl { + /// Implements `PartialEq` for primitive types. + /// + /// Primitive types have a compiler-defined primitive implementation of `==` and `!=`. + /// This implements the `PartialEq` trait in terms of those primitive implementations. + /// + /// NOTE: Calling this on a non-primitive type (such as `()`) + /// leads to an infinitely-looping self-recursive implementation. + macro_rules! impl_partial_eq_for_primitive { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialEq for $t { #[inline] fn eq(&self, other: &Self) -> bool { *self == *other } + // Override the default to use the primitive implementation for `!=`. #[inline] fn ne(&self, other: &Self) -> bool { *self != *other } } )*) } + impl_partial_eq_for_primitive! { + bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 + } + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialEq for () { @@ -1895,10 +1919,6 @@ mod impls { } } - partial_eq_impl! { - bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 - } - macro_rules! eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 8a374f015a958..a2d367931ea9b 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2392,7 +2392,7 @@ macro_rules! int_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `<<` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shl`] which has nicer behaviour. @@ -2429,7 +2429,7 @@ macro_rules! int_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `>>` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shr`] which has nicer behaviour. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index cfb5eb54fb6bc..b33b2b5db1a32 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2827,7 +2827,7 @@ macro_rules! uint_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `<<` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shl`] which has nicer behaviour. @@ -2871,7 +2871,7 @@ macro_rules! uint_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `>>` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shr`] which has nicer behaviour. diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 163e8bf714b1d..9e7a8d4472d81 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2082,10 +2082,7 @@ impl Option { /// assert_eq!(o2.into_flat_iter().collect::>(), Vec::<&usize>::new()); /// ``` #[unstable(feature = "option_into_flat_iter", issue = "148441")] - pub fn into_flat_iter(self) -> OptionFlatten - where - T: IntoIterator, - { + pub fn into_flat_iter(self) -> OptionFlatten { OptionFlatten { iter: self.map(IntoIterator::into_iter) } } } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 4b02e73a4dcc3..25889994be977 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3692,18 +3692,22 @@ impl [T] { self.partition_dedup_by(|a, b| a == b) } - /// Moves all but the first of consecutive elements to the end of the slice satisfying - /// a given equality relation. + /// Moves all but the first of consecutive elements to the end of the slice that are + /// "equal" according to the given predicate function. /// /// Returns two slices. The first contains no consecutive repeated elements. /// The second contains all the duplicates in no specified order. /// - /// The `same_bucket` function is passed references to two elements from the slice and - /// must determine if the elements compare equal. The elements are passed in opposite order - /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is moved - /// at the end of the slice. + /// The predicate `same_bucket(x, p)` is passed references to two elements from + /// the slice and must determine if the elements compare equal. The element `p` occurs + /// *before* `x` in the slice (`[.., p, .., x, ..]`), so `same_bucket(x, p)` + /// is receiving them in reversed order. /// - /// If the slice is sorted, the first returned slice contains no duplicates. + /// If the slice is sorted, the first returned slice contains no duplicates. For more + /// complicated predicates however, the order (ascending vs. descending) can matter. + /// + /// Both references passed to `same_bucket` are mutable. + /// This allows merged elements in the first slice by mutating `p` and returning `true`. /// /// # Examples /// @@ -3712,7 +3716,7 @@ impl [T] { /// /// let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"]; /// - /// let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b)); + /// let (dedup, duplicates) = slice.partition_dedup_by(|x, p| x.eq_ignore_ascii_case(p)); /// /// assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]); /// assert_eq!(duplicates, ["bar", "Foo", "BAZ"]); @@ -3793,7 +3797,7 @@ impl [T] { // are less than `len`, thus are inside `self`. `prev_ptr_write` points to // one element before `ptr_write`, but `next_write` starts at 1, so // `prev_ptr_write` is never less than 0 and is inside the slice. - // This fulfils the requirements for dereferencing `ptr_read`, `prev_ptr_write` + // This fulfills the requirements for dereferencing `ptr_read`, `prev_ptr_write` // and `ptr_write`, and for using `ptr.add(next_read)`, `ptr.add(next_write - 1)` // and `prev_ptr_write.offset(1)`. // diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 7a576e083df7c..753ca5aba561c 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -63,14 +63,15 @@ #![deny(unsafe_op_in_unsafe_fn)] #![stable(feature = "alloc_module", since = "1.28.0")] -use core::ptr::NonNull; -use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; -use core::{hint, mem, ptr}; - #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] pub use alloc_crate::alloc::*; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::sys::alloc as imp; +use crate::{hint, mem, ptr}; + /// The default memory allocator provided by the operating system. /// /// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows, @@ -145,11 +146,7 @@ impl System { 0 => Ok(layout.dangling_ptr().cast_slice(0)), // SAFETY: `layout` is non-zero in size, size => unsafe { - let raw_ptr = if zeroed { - GlobalAlloc::alloc_zeroed(self, layout) - } else { - GlobalAlloc::alloc(self, layout) - }; + let raw_ptr = if zeroed { imp::alloc_zeroed(layout) } else { imp::alloc(layout) }; let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(ptr.cast_slice(size)) }, @@ -182,7 +179,7 @@ impl System { // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. hint::assert_unchecked(new_size >= old_layout.size()); - let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); + let raw_ptr = imp::realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); @@ -205,8 +202,8 @@ impl System { } } -// The Allocator impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl, -// which is in `std::sys::*::alloc`. +// The Allocator impl checks the layout size to be non-zero and forwards to the +// platform functions in `std::sys::*::alloc`. #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Allocator for System { #[inline] @@ -224,7 +221,7 @@ unsafe impl Allocator for System { if layout.size() != 0 { // SAFETY: `layout` is non-zero in size, // other conditions must be upheld by the caller - unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } + unsafe { imp::dealloc(ptr.as_ptr(), layout) } } } @@ -274,7 +271,7 @@ unsafe impl Allocator for System { // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. hint::assert_unchecked(new_size <= old_layout.size()); - let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); + let raw_ptr = imp::realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(ptr.cast_slice(new_size)) }, @@ -294,6 +291,9 @@ unsafe impl Allocator for System { } } +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl GlobalAllocator for System {} + static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. @@ -435,7 +435,12 @@ pub fn rust_oom(layout: Layout) -> ! { #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] pub mod __default_lib_allocator { - use super::{GlobalAlloc, Layout, System}; + use super::Layout; + // We call the system functions directly to avoid any overheads introduced + // by the roundtrip through `impl Allocator for System` and + // `impl GlobalAlloc for A`. + use crate::sys::alloc as imp; + // These magic symbol names are used as a fallback for implementing the // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is // no `#[global_allocator]` attribute. @@ -452,7 +457,7 @@ pub mod __default_lib_allocator { // `GlobalAlloc::alloc`. unsafe { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc(layout) + imp::alloc(layout) } } @@ -460,7 +465,7 @@ pub mod __default_lib_allocator { pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { // SAFETY: see the guarantees expected by `Layout::from_size_align` and // `GlobalAlloc::dealloc`. - unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } + unsafe { imp::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } } #[rustc_std_internal_symbol] @@ -474,7 +479,7 @@ pub mod __default_lib_allocator { // `GlobalAlloc::realloc`. unsafe { let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr, old_layout, new_size) + imp::realloc(ptr, old_layout, new_size) } } @@ -484,7 +489,7 @@ pub mod __default_lib_allocator { // `GlobalAlloc::alloc_zeroed`. unsafe { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc_zeroed(layout) + imp::alloc_zeroed(layout) } } } diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index c226a0dc2403f..4ed5c43616a29 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -12,7 +12,7 @@ use crate::fs; use crate::marker::PhantomData; use crate::mem::ManuallyDrop; #[cfg(not(any( - target_arch = "wasm32", + all(target_arch = "wasm32", not(target_os = "emscripten")), target_env = "sgx", target_os = "hermit", target_os = "trusty", @@ -104,7 +104,7 @@ impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. #[cfg(not(any( - target_arch = "wasm32", + all(target_arch = "wasm32", not(target_os = "emscripten")), target_os = "hermit", target_os = "trusty", target_os = "motor" @@ -131,7 +131,11 @@ impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - #[cfg(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty"))] + #[cfg(any( + all(target_arch = "wasm32", not(target_os = "emscripten")), + target_os = "hermit", + target_os = "trusty" + ))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> io::Result { Err(io::Error::UNSUPPORTED_PLATFORM) diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 0385e5513ea08..19fdf8ba2fb03 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -214,7 +214,7 @@ fn replace_stdio_fd(this: BorrowedFd<'_>, other: OwnedFd) -> io::Result<()> { cvt(unsafe { libc::__wasilibc_fd_renumber(other.as_raw_fd(), this.as_raw_fd()) }).map(|_| ()) } not(any( - target_arch = "wasm32", + all(target_arch = "wasm32", not(target_os = "emscripten")), target_os = "hermit", target_os = "trusty", target_os = "motor" diff --git a/library/std/src/sys/alloc/hermit.rs b/library/std/src/sys/alloc/hermit.rs index 77f8200a70a64..9afcb315f4ab5 100644 --- a/library/std/src/sys/alloc/hermit.rs +++ b/library/std/src/sys/alloc/hermit.rs @@ -1,27 +1,24 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let size = layout.size(); - let align = layout.align(); - unsafe { hermit_abi::malloc(size, align) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::malloc(size, align) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - let size = layout.size(); - let align = layout.align(); - unsafe { - hermit_abi::free(ptr, size, align); - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + let size = layout.size(); + let align = layout.align(); + unsafe { + hermit_abi::free(ptr, size, align); } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - let size = layout.size(); - let align = layout.align(); - unsafe { hermit_abi::realloc(ptr, size, align, new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::realloc(ptr, size, align, new_size) } } diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index f2f1d1c7feceb..73d35781f09bf 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; // The minimum alignment guaranteed by the architecture. This value is used to @@ -47,21 +47,16 @@ const MIN_ALIGN: usize = if cfg!(any( }; #[allow(dead_code)] -unsafe fn realloc_fallback( - alloc: &System, - ptr: *mut u8, - old_layout: Layout, - new_size: usize, -) -> *mut u8 { +unsafe fn realloc_fallback(ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 { // SAFETY: Docs for GlobalAlloc::realloc require this to be valid unsafe { let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(alloc, new_layout); + let new_ptr = alloc(new_layout); if !new_ptr.is_null() { let size = usize::min(old_layout.size(), new_size); ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(alloc, ptr, old_layout); + dealloc(ptr, old_layout); } new_ptr @@ -76,35 +71,69 @@ cfg_select! { target_os = "trusty", ) => { mod unix; + use unix as imp; } target_os = "windows" => { mod windows; + use windows as imp; } target_os = "hermit" => { mod hermit; + use hermit as imp; } target_os = "motor" => { mod motor; + use motor as imp; } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; + use sgx as imp; } target_os = "solid_asp3" => { mod solid; + use solid as imp; } target_os = "uefi" => { mod uefi; + use uefi as imp; } target_os = "vexos" => { mod vexos; + use vexos as imp; } target_family = "wasm" => { mod wasm; + use wasm as imp; } target_os = "xous" => { mod xous; + use xous as imp; } target_os = "zkvm" => { mod zkvm; + use zkvm as imp; + } +} + +pub use imp::{alloc, dealloc, realloc}; + +cfg_select! { + any( + target_os = "hermit", + target_os = "solid_asp3", + target_os = "uefi", + target_os = "zkvm", + ) => { + #[inline] + pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + let ptr = unsafe { alloc(layout) }; + if !ptr.is_null() { + unsafe { ptr.write_bytes(0, layout.size()) }; + } + ptr + } + } + _ => { + pub use imp::alloc_zeroed; } } diff --git a/library/std/src/sys/alloc/motor.rs b/library/std/src/sys/alloc/motor.rs index 271e3c40c26ae..090dc198bb500 100644 --- a/library/std/src/sys/alloc/motor.rs +++ b/library/std/src/sys/alloc/motor.rs @@ -1,28 +1,13 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: same requirements as in GlobalAlloc::alloc. - moto_rt::alloc::alloc(layout) - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: same requirements as in GlobalAlloc::alloc_zeroed. - moto_rt::alloc::alloc_zeroed(layout) - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: same requirements as in GlobalAlloc::dealloc. - unsafe { moto_rt::alloc::dealloc(ptr, layout) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + moto_rt::alloc::alloc(layout) +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: same requirements as in GlobalAlloc::realloc. - unsafe { moto_rt::alloc::realloc(ptr, layout, new_size) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + moto_rt::alloc::alloc_zeroed(layout) } + +pub use moto_rt::alloc::{dealloc, realloc}; diff --git a/library/std/src/sys/alloc/sgx.rs b/library/std/src/sys/alloc/sgx.rs index afdef7a5cb647..c20761259048e 100644 --- a/library/std/src/sys/alloc/sgx.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -1,4 +1,4 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sys::pal::abi::mem as sgx_mem; @@ -57,31 +57,28 @@ unsafe impl dlmalloc::Allocator for Sgx { } } -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().malloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().malloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().calloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().calloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().free(ptr, layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().free(ptr, layout.size(), layout.align()) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) } } // The following functions are needed by libunwind. These symbols are named diff --git a/library/std/src/sys/alloc/solid.rs b/library/std/src/sys/alloc/solid.rs index 47cfa2eb1162b..5d3f396f0dd11 100644 --- a/library/std/src/sys/alloc/solid.rs +++ b/library/std/src/sys/alloc/solid.rs @@ -1,30 +1,27 @@ use super::{MIN_ALIGN, realloc_fallback}; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::malloc(layout.size()) as *mut u8 } - } else { - unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + unsafe { libc::malloc(layout.size()) as *mut u8 } + } else { + unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 } } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - unsafe { libc::free(ptr as *mut libc::c_void) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, _layout: Layout) { + unsafe { libc::free(ptr as *mut libc::c_void) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - unsafe { - if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 - } else { - realloc_fallback(self, ptr, layout, new_size) - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + unsafe { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + realloc_fallback(ptr, layout, new_size) } } } diff --git a/library/std/src/sys/alloc/uefi.rs b/library/std/src/sys/alloc/uefi.rs index 5221876e90866..aa7fcc9fe110c 100644 --- a/library/std/src/sys/alloc/uefi.rs +++ b/library/std/src/sys/alloc/uefi.rs @@ -3,47 +3,48 @@ use r_efi::protocols::loaded_image; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::sync::OnceLock; use crate::sys::pal::helpers; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - static EFI_MEMORY_TYPE: OnceLock = OnceLock::new(); - - // Return null pointer if boot services are not available - if crate::os::uefi::env::boot_services().is_none() { - return crate::ptr::null_mut(); - } - - // If boot services is valid then SystemTable is not null. - let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); - - // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this - // will never fail. - let mem_type = EFI_MEMORY_TYPE.get_or_init(|| { - let protocol = helpers::image_handle_protocol::( - loaded_image::PROTOCOL_GUID, - ) - .unwrap(); - // Gives allocations the memory type that the data sections were loaded as. - unsafe { (*protocol.as_ptr()).image_data_type } - }); - - // The caller must ensure non-0 layout - unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) } +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + static EFI_MEMORY_TYPE: OnceLock = OnceLock::new(); + + // Return null pointer if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return crate::ptr::null_mut(); } - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // Do nothing if boot services are not available - if crate::os::uefi::env::boot_services().is_none() { - return; - } + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + + // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this + // will never fail. + let mem_type = EFI_MEMORY_TYPE.get_or_init(|| { + let protocol = + helpers::image_handle_protocol::(loaded_image::PROTOCOL_GUID) + .unwrap(); + // Gives allocations the memory type that the data sections were loaded as. + unsafe { (*protocol.as_ptr()).image_data_type } + }); + + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) } +} - // If boot services is valid then SystemTable is not null. - let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); - // The caller must ensure non-0 layout - unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // Do nothing if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return; } + + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } +} + +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: this is just a `pub` wrapper. + unsafe { super::realloc_fallback(ptr, layout, new_size) } } diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index 3d369b08abc77..d6de8bf28c44c 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -1,60 +1,57 @@ use super::{MIN_ALIGN, realloc_fallback}; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // jemalloc provides alignment less than MIN_ALIGN for small allocations. - // So only rely on MIN_ALIGN if size >= align. - // Also see and - // . - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::malloc(layout.size()) as *mut u8 } - } else { - // `posix_memalign` returns a non-aligned value if supplied a very - // large alignment on older versions of Apple's platforms (unknown - // exactly which version range, but the issue is definitely - // present in macOS 10.14 and iOS 13.3). - // - // - #[cfg(target_vendor = "apple")] - { - if layout.align() > (1 << 31) { - return ptr::null_mut(); - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // jemalloc provides alignment less than MIN_ALIGN for small allocations. + // So only rely on MIN_ALIGN if size >= align. + // Also see and + // . + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + unsafe { libc::malloc(layout.size()) as *mut u8 } + } else { + // `posix_memalign` returns a non-aligned value if supplied a very + // large alignment on older versions of Apple's platforms (unknown + // exactly which version range, but the issue is definitely + // present in macOS 10.14 and iOS 13.3). + // + // + #[cfg(target_vendor = "apple")] + { + if layout.align() > (1 << 31) { + return ptr::null_mut(); } - unsafe { aligned_malloc(&layout) } } + unsafe { aligned_malloc(&layout) } } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // See the comment above in `alloc` for why this check looks the way it does. - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::calloc(layout.size(), 1) as *mut u8 } - } else { - let ptr = unsafe { self.alloc(layout) }; - if !ptr.is_null() { - unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; - } - ptr +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // See the comment above in `alloc` for why this check looks the way it does. + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + unsafe { libc::calloc(layout.size(), 1) as *mut u8 } + } else { + let ptr = unsafe { alloc(layout) }; + if !ptr.is_null() { + unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; } + ptr } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - unsafe { libc::free(ptr as *mut libc::c_void) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, _layout: Layout) { + unsafe { libc::free(ptr as *mut libc::c_void) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } - } else { - unsafe { realloc_fallback(self, ptr, layout, new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } + } else { + unsafe { realloc_fallback(ptr, layout, new_size) } } } diff --git a/library/std/src/sys/alloc/vexos.rs b/library/std/src/sys/alloc/vexos.rs index 5ccf6625ed626..6aba6dc474911 100644 --- a/library/std/src/sys/alloc/vexos.rs +++ b/library/std/src/sys/alloc/vexos.rs @@ -1,7 +1,7 @@ // FIXME(static_mut_refs): use raw pointers instead of references #![allow(static_mut_refs)] -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; use crate::sync::atomic::{AtomicBool, Ordering}; @@ -60,37 +60,34 @@ unsafe impl dlmalloc::Allocator for Vexos { } } -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which - // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. - // Calling malloc() is safe because preconditions on this function match the trait method preconditions. - unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which - // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. - // Calling calloc() is safe because preconditions on this function match the trait method preconditions. - unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which - // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. - // Calling free() is safe because preconditions on this function match the trait method preconditions. - unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which - // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. - // Calling realloc() is safe because preconditions on this function match the trait method preconditions. - unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which + // guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } } diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index 48e2fdd4eccec..995230aebaa01 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -18,7 +18,7 @@ use core::cell::SyncUnsafeCell; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; struct SyncDlmalloc(dlmalloc::Dlmalloc); unsafe impl Sync for SyncDlmalloc {} @@ -26,39 +26,36 @@ unsafe impl Sync for SyncDlmalloc {} static DLMALLOC: SyncUnsafeCell = SyncUnsafeCell::new(SyncDlmalloc(dlmalloc::Dlmalloc::new())); -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling malloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.malloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.malloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling calloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.calloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.calloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling free() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.free(ptr, layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.free(ptr, layout.size(), layout.align()) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling realloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.realloc(ptr, layout.size(), layout.align(), new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.realloc(ptr, layout.size(), layout.align(), new_size) } } #[cfg(target_feature = "atomics")] diff --git a/library/std/src/sys/alloc/windows.rs b/library/std/src/sys/alloc/windows.rs index 9336a6ec085aa..1d75cbd9d54f1 100644 --- a/library/std/src/sys/alloc/windows.rs +++ b/library/std/src/sys/alloc/windows.rs @@ -1,5 +1,18 @@ +//! Implements `System` on Windows. +//! +//! All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the +//! following properties: +//! +//! If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN` +//! the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block. +//! +//! If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN` +//! the pointer will be aligned to the specified alignment and not point to the start of the allocated block. +//! Instead there will be a header readable directly before the returned pointer, containing the actual +//! location of the start of the block. + use super::{MIN_ALIGN, realloc_fallback}; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ffi::c_void; use crate::mem::MaybeUninit; use crate::ptr; @@ -150,70 +163,56 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { } } -// All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the -// following properties: -// -// If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN` -// the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block. -// -// If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN` -// the pointer will be aligned to the specified alignment and not point to the start of the allocated block. -// Instead there will be a header readable directly before the returned pointer, containing the actual -// location of the start of the block. -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` - let zeroed = false; - unsafe { allocate(layout, zeroed) } - } +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = false; + unsafe { allocate(layout, zeroed) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` - let zeroed = true; - unsafe { allocate(layout, zeroed) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = true; + unsafe { allocate(layout, zeroed) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - let block = { - if layout.align() <= MIN_ALIGN { - ptr - } else { - // The location of the start of the block is stored in the padding before `ptr`. +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + let block = { + if layout.align() <= MIN_ALIGN { + ptr + } else { + // The location of the start of the block is stored in the padding before `ptr`. - // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null - // and have a header readable directly before it. - unsafe { ptr::read((ptr as *mut Header).sub(1)).0 } - } - }; + // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null + // and have a header readable directly before it. + unsafe { ptr::read((ptr as *mut Header).sub(1)).0 } + } + }; + // because `ptr` has been successfully allocated with this allocator, + // there must be a valid process heap. + let heap = get_process_heap(); + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, + // `block` is a pointer to the start of an allocated block. + unsafe { HeapFree(heap, 0, block.cast::()) }; +} + +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN { // because `ptr` has been successfully allocated with this allocator, // there must be a valid process heap. let heap = get_process_heap(); // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, - // `block` is a pointer to the start of an allocated block. - unsafe { HeapFree(heap, 0, block.cast::()) }; - } - - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN { - // because `ptr` has been successfully allocated with this allocator, - // there must be a valid process heap. - let heap = get_process_heap(); - - // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, - // `ptr` is a pointer to the start of an allocated block. - // The returned pointer points to the start of an allocated block. - unsafe { HeapReAlloc(heap, 0, ptr.cast::(), new_size).cast::() } - } else { - // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will - // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` - unsafe { realloc_fallback(self, ptr, layout, new_size) } - } + // `ptr` is a pointer to the start of an allocated block. + // The returned pointer points to the start of an allocated block. + unsafe { HeapReAlloc(heap, 0, ptr.cast::(), new_size).cast::() } + } else { + // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will + // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` + unsafe { realloc_fallback(ptr, layout, new_size) } } } diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index af2dcabc1a23b..74229351f6d22 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -1,7 +1,7 @@ // FIXME(static_mut_refs): use raw pointers instead of references #![allow(static_mut_refs)] -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; #[cfg(not(test))] #[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE")] @@ -13,39 +13,36 @@ unsafe extern "Rust" { static mut DLMALLOC: dlmalloc::Dlmalloc; } -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling malloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling calloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling free() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling realloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } } mod lock { diff --git a/library/std/src/sys/alloc/zkvm.rs b/library/std/src/sys/alloc/zkvm.rs index a600cfa2220dd..7f39d7fed777e 100644 --- a/library/std/src/sys/alloc/zkvm.rs +++ b/library/std/src/sys/alloc/zkvm.rs @@ -1,15 +1,18 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::sys::pal::abi; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - unsafe { abi::sys_alloc_aligned(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + unsafe { abi::sys_alloc_aligned(layout.size(), layout.align()) } +} + +#[inline] +pub unsafe fn dealloc(_ptr: *mut u8, _layout: Layout) { + // this allocator never deallocates memory +} - #[inline] - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { - // this allocator never deallocates memory - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: this is just a `pub` wrapper. + unsafe { super::realloc_fallback(ptr, layout, new_size) } } diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs index cc8cda9aec045..1b6bfea27d3b0 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -15,22 +15,14 @@ cfg_select! { mod solid; pub use solid::*; } - target_os = "teeos" => { - mod teeos; - pub use teeos::*; - } target_os = "uefi" => { mod uefi; pub use uefi::*; } - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi", target_os = "teeos") => { mod unix; pub use unix::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; - } target_os = "windows" => { mod windows; pub use windows::*; diff --git a/library/std/src/sys/io/error/teeos.rs b/library/std/src/sys/io/error/teeos.rs deleted file mode 100644 index 18826ffc3894f..0000000000000 --- a/library/std/src/sys/io/error/teeos.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::io; - -pub fn errno() -> i32 { - unsafe { (*libc::__errno_location()) as i32 } -} - -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -// Note: code below is 1:1 copied from unix/mod.rs -pub fn decode_error_kind(errno: i32) -> io::ErrorKind { - use io::ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - libc::ENOTEMPTY => DirectoryNotEmpty, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - - libc::EACCES | libc::EPERM => PermissionDenied, - - // These two constants can have the same value on some systems, - // but different values on others, so we can't use a match - // clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - -pub fn error_string(_errno: i32) -> String { - "error string unimplemented".to_string() -} diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs index 85e3c09782ae2..2feeefa2530c8 100644 --- a/library/std/src/sys/io/error/unix.rs +++ b/library/std/src/sys/io/error/unix.rs @@ -1,8 +1,15 @@ -use crate::ffi::{CStr, c_char, c_int}; +use crate::ffi::c_int; +#[cfg(not(target_os = "teeos"))] +use crate::ffi::{CStr, c_char}; use crate::io; unsafe extern "C" { - #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] + #[cfg(not(any( + target_os = "dragonfly", + target_os = "vxworks", + target_os = "rtems", + target_os = "wasi" + )))] #[cfg_attr( any( target_os = "linux", @@ -10,6 +17,7 @@ unsafe extern "C" { target_os = "fuchsia", target_os = "l4re", target_os = "hurd", + target_os = "teeos", ), link_name = "__errno_location" )] @@ -37,7 +45,12 @@ unsafe extern "C" { } /// Returns the platform-specific value of errno -#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] +#[cfg(not(any( + target_os = "dragonfly", + target_os = "vxworks", + target_os = "rtems", + target_os = "wasi" +)))] #[inline] pub fn errno() -> i32 { unsafe { (*errno_location()) as i32 } @@ -45,7 +58,12 @@ pub fn errno() -> i32 { /// Sets the platform-specific value of errno // needed for readdir and syscall! -#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] +#[cfg(not(any( + target_os = "dragonfly", + target_os = "vxworks", + target_os = "rtems", + target_os = "wasi", +)))] #[allow(dead_code)] // but not all target cfgs actually end up using it #[inline] pub fn set_errno(e: i32) { @@ -94,6 +112,25 @@ pub fn set_errno(e: i32) { } } +#[cfg(target_os = "wasi")] +unsafe extern "C" { + #[thread_local] + #[link_name = "errno"] + static mut libc_errno: libc::c_int; +} + +#[cfg(target_os = "wasi")] +pub fn errno() -> i32 { + unsafe { libc_errno as i32 } +} + +#[cfg(target_os = "wasi")] +pub fn set_errno(val: i32) { + unsafe { + libc_errno = val; + } +} + #[inline] pub fn is_interrupted(errno: i32) -> bool { errno == libc::EINTR @@ -153,8 +190,9 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { } /// Gets a detailed string description for the given error number. +#[cfg(any(target_family = "unix", target_os = "wasi"))] pub fn error_string(errno: i32) -> String { - const TMPBUF_SZ: usize = 128; + const TMPBUF_SZ: usize = if cfg!(target_os = "wasi") { 1024 } else { 128 }; unsafe extern "C" { #[cfg_attr( @@ -186,3 +224,8 @@ pub fn error_string(errno: i32) -> String { String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() } } + +#[cfg(target_os = "teeos")] +pub fn error_string(_errno: i32) -> String { + "error string unimplemented".to_string() +} diff --git a/library/std/src/sys/io/error/wasi.rs b/library/std/src/sys/io/error/wasi.rs deleted file mode 100644 index 3a0fa37e979bc..0000000000000 --- a/library/std/src/sys/io/error/wasi.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::ffi::CStr; -use crate::io as std_io; - -unsafe extern "C" { - #[thread_local] - #[link_name = "errno"] - static mut libc_errno: libc::c_int; -} - -pub fn errno() -> i32 { - unsafe { libc_errno as i32 } -} - -pub fn set_errno(val: i32) { - unsafe { - libc_errno = val; - } -} - -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { - use std_io::ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - libc::EINPROGRESS => InProgress, - libc::EMFILE | libc::ENFILE => TooManyOpenFiles, - libc::EOPNOTSUPP => Unsupported, - libc::EACCES | libc::EPERM => PermissionDenied, - libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - -pub fn error_string(errno: i32) -> String { - let mut buf = [0 as libc::c_char; 1024]; - - let p = buf.as_mut_ptr(); - unsafe { - if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 { - panic!("strerror_r failure"); - } - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() - } -} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index fe0c103952414..58c8dc43e12ae 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -1,11 +1,11 @@ #![allow(unsafe_op_in_unsafe_fn)] -mod alloc; mod configure_builtins; mod helpers; mod pal; mod personality; +pub mod alloc; pub mod args; pub mod backtrace; pub mod cmath; diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 019d5629d6d6e..0e20a4192b1b9 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -204,6 +204,35 @@ cfg_select! { } } _ => { + #[rustc_force_inline] + unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { + cfg_select! { + all(target_abi = "pauthtest", target_arch = "aarch64") => { + // DWARF register number for SP on AArch64. + const SP_REG: i32 = 31; + + unsafe { + let sp = uw::_Unwind_GetGR(context, SP_REG).addr() as u64; + let mut addr = lpad.addr(); + + // `pacib` corresponds to `ptrauth_key_process_dependent_code` in . + core::arch::asm!( + "pacib {addr}, {sp}", + addr = inout(reg) addr, + sp = in(reg) sp, + options(nostack, preserves_flags) + ); + + lpad.with_addr(addr) + } + } + _ => { + let _ = context; + lpad + } + } + } + /// Default personality routine, which is used directly on most targets /// and indirectly on Windows x86_64 and AArch64 via SEH. unsafe extern "C" fn rust_eh_personality_impl( @@ -239,7 +268,8 @@ cfg_select! { exception_object.cast(), ); uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); + let maybe_signed_lpad = sign_lpad(context, lpad); + uw::_Unwind_SetIP(context, maybe_signed_lpad); uw::_URC_INSTALL_CONTEXT } EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index dad1ea6c57377..9643c3b7bdad8 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -13,10 +13,16 @@ fn main() { fn parent() { let me = env::current_exe().unwrap(); + // If a custom `runner` is set up for the current target, we'll be + // executing `./runner ./test`, not just `./test`. For such a case, + // use the same arguments for child to avoid executing `runner` + // without an actual executable. + let args = env::args(); let (rx, tx) = pipe().unwrap(); assert!( process::Command::new(me) + .args(args) .env("I_AM_THE_CHILD", "1") .stdout(tx) .status() diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 93f73ccad3ea4..80e712a2388a1 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -26,8 +26,16 @@ fn issue_15149() { env::join_paths(paths).unwrap() }; - let child_output = - process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap(); + // If a custom `runner` is set up for the current target, we'll be executing `./runner ./test`, + // not just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without an actual executable. + let args = env::args(); + let child_output = process::Command::new("mytest") + .args(args) + .env("PATH", &path) + .arg("child") + .output() + .unwrap(); assert!( child_output.status.success(), diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 6333b32ab8ad4..9e102a15e8ff7 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -285,12 +285,13 @@ jobs: include: - target: aarch64_be-unknown-linux-gnu build_std: true - - target: x86_64-unknown-linux-gnu - cc: icx - profile: dev - - target: x86_64-unknown-linux-gnu - cc: icx - profile: release + # getting the icx compiler hits network failures. + # - target: x86_64-unknown-linux-gnu + # cc: icx + # profile: dev + # - target: x86_64-unknown-linux-gnu + # cc: icx + # profile: release exclude: - target: armv7-unknown-linux-gnueabihf cc: gcc @@ -318,7 +319,7 @@ jobs: # Check that the generated files agree with the checked-in versions. check-stdarch-gen: needs: [style] - name: Check stdarch-gen-{arm, loongarch, hexagon, hexagon-scalar} output + name: Check stdarch-gen-{arm, loongarch, hexagon} output runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -342,11 +343,7 @@ jobs: run: | cargo run -p stdarch-gen-hexagon --release git diff --exit-code - - name: Check hexagon scalar - run: | - cargo run -p stdarch-gen-hexagon-scalar --release - git diff --exit-code - + # Run some tests with Miri. Most stdarch functions use platform-specific intrinsics # that Miri does not support. Also Miri is reltively slow. # diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index c8cfc21a5a1a1..cf6ce0d38e40c 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -849,13 +849,6 @@ dependencies = [ "stdarch-gen-common", ] -[[package]] -name = "stdarch-gen-hexagon-scalar" -version = "0.1.0" -dependencies = [ - "regex", -] - [[package]] name = "stdarch-gen-loongarch" version = "0.1.0" @@ -869,7 +862,6 @@ version = "0.1.0" dependencies = [ "assert-instr-macro", "cc", - "cfg-if", "rustc-demangle", "simd-test-macro", "wasmprinter", diff --git a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile index 1b61dd0c1b87a..0ae4172f9dd37 100644 --- a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xz-utils \ wget -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm @@ -19,5 +19,5 @@ ENV CLANG_PATH="/llvm/bin/clang" ENV GCC_PATH=aarch64-linux-gnu-gcc ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -cpu max -L /usr/aarch64-linux-gnu" \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -cpu max,sve512=on -L /usr/aarch64-linux-gnu" \ OBJDUMP=aarch64-linux-gnu-objdump diff --git a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile index 70acc2d22a418..0b7a59cc08d83 100644 --- a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile @@ -19,7 +19,7 @@ RUN curl -L "https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/bin RUN tar -xvf "${TOOLCHAIN}.tar.xz" RUN mkdir /toolchains && mv "./${TOOLCHAIN}" /toolchains -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm diff --git a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile index 3c8a1b5add27f..f4240be7bb47b 100644 --- a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile +++ b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile @@ -10,7 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ wget -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm diff --git a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index efbb2b0853371..1ac46dc407395 100644 --- a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -13,7 +13,7 @@ RUN wget http://ci-mirrors.rust-lang.org/sde-external-10.8.0-2026-03-15-lin.tar. RUN mkdir intel-sde RUN tar -xJf sde.tar.xz --strip-components=1 -C intel-sde -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 8de7a4cfa5221..3966aa88d8f41 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -45,21 +45,25 @@ case ${TARGET} in aarch64_be*) export CFLAGS="-I${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc/usr/include --sysroot={AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc -Wno-nonportable-vector-initialization" ARCH=aarch64_be + RUNTIME_RUSTFLAGS= ;; aarch64*) export CFLAGS="-I/usr/aarch64-linux-gnu/include/" ARCH=aarch64 + RUNTIME_RUSTFLAGS=-Ctarget-feature=+sve,+sve2 ;; armv7*) export CFLAGS="-I/usr/arm-linux-gnueabihf/include/" ARCH=arm + RUNTIME_RUSTFLAGS= ;; x86_64*) export CFLAGS="-I/usr/include/x86_64-linux-gnu/" ARCH=x86 + RUNTIME_RUSTFLAGS= ;; *) ;; @@ -86,5 +90,5 @@ case "${TARGET}" in ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" \ - --tests "$@" +RUSTFLAGS="${RUNTIME_RUSTFLAGS}" cargo test --manifest-path=rust_programs/Cargo.toml \ + --target "${TARGET}" --profile "${PROFILE}" --tests --no-fail-fast "$@" diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index ac3070918a025..4e5547b1d083e 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -42211,19 +42211,6 @@ pub fn svtmad_f64(op1: svfloat64_t, op2: svfloat64_t) -> svfloa unsafe { _svtmad_f64(op1, op2, IMM3) } } #[doc = "Interleave even elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(trn1))] -pub fn svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv16i1")] - fn _svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svtrn1_b8(op1, op2) } -} -#[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -42231,10 +42218,10 @@ pub fn svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn1))] pub fn svtrn1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv8i1")] - fn _svtrn1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.b16")] + fn _svtrn1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b32)"] @@ -42244,10 +42231,10 @@ pub fn svtrn1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn1))] pub fn svtrn1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv4i1")] - fn _svtrn1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.b32")] + fn _svtrn1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b64)"] @@ -42257,10 +42244,10 @@ pub fn svtrn1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn1))] pub fn svtrn1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv2i1")] - fn _svtrn1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.b64")] + fn _svtrn1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1[_f32])"] @@ -42376,6 +42363,19 @@ pub fn svtrn1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svtrn1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svtrn1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave even elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(trn1))] +pub fn svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv16i1")] + fn _svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svtrn1_b8(op1, op2) } +} #[doc = "Interleave even quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1q[_f32])"] #[inline] @@ -42491,19 +42491,6 @@ pub fn svtrn1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svtrn1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Interleave odd elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(trn2))] -pub fn svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv16i1")] - fn _svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svtrn2_b8(op1, op2) } -} -#[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -42511,10 +42498,10 @@ pub fn svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn2))] pub fn svtrn2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv8i1")] - fn _svtrn2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.b16")] + fn _svtrn2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b32)"] @@ -42524,10 +42511,10 @@ pub fn svtrn2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn2))] pub fn svtrn2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv4i1")] - fn _svtrn2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.b32")] + fn _svtrn2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b64)"] @@ -42537,10 +42524,10 @@ pub fn svtrn2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn2))] pub fn svtrn2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv2i1")] - fn _svtrn2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.b64")] + fn _svtrn2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2[_f32])"] @@ -42656,6 +42643,19 @@ pub fn svtrn2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svtrn2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svtrn2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave odd elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(trn2))] +pub fn svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv16i1")] + fn _svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svtrn2_b8(op1, op2) } +} #[doc = "Interleave odd quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2q[_f32])"] #[inline] diff --git a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs index 477414de74b85..a42c5c5e10f30 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon scalar intrinsics //! //! This module provides intrinsics for scalar (non-HVX) Hexagon DSP operations, diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs index 6a6a3ae924d85..311479750601a 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs @@ -199,10 +199,6 @@ unsafe extern "unadjusted" { fn __lasx_xvfclass_s(a: __v8f32) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvfclass.d"] fn __lasx_xvfclass_d(a: __v4f64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvfrecip.s"] - fn __lasx_xvfrecip_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrecip.d"] - fn __lasx_xvfrecip_d(a: __v4f64) -> __v4f64; #[link_name = "llvm.loongarch.lasx.xvfrecipe.s"] fn __lasx_xvfrecipe_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvfrecipe.d"] @@ -215,10 +211,6 @@ unsafe extern "unadjusted" { fn __lasx_xvfrint_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvfrint.d"] fn __lasx_xvfrint_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrsqrt.s"] - fn __lasx_xvfrsqrt_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrsqrt.d"] - fn __lasx_xvfrsqrt_d(a: __v4f64) -> __v4f64; #[link_name = "llvm.loongarch.lasx.xvflogb.s"] fn __lasx_xvflogb_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvflogb.d"] @@ -265,22 +257,6 @@ unsafe extern "unadjusted" { fn __lasx_xvreplve_d(a: __v4i64, b: i32) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvpermi.w"] fn __lasx_xvpermi_w(a: __v8i32, b: __v8i32, c: u32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvmuh.b"] - fn __lasx_xvmuh_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvmuh.h"] - fn __lasx_xvmuh_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvmuh.w"] - fn __lasx_xvmuh_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvmuh.d"] - fn __lasx_xvmuh_d(a: __v4i64, b: __v4i64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvmuh.bu"] - fn __lasx_xvmuh_bu(a: __v32u8, b: __v32u8) -> __v32u8; - #[link_name = "llvm.loongarch.lasx.xvmuh.hu"] - fn __lasx_xvmuh_hu(a: __v16u16, b: __v16u16) -> __v16u16; - #[link_name = "llvm.loongarch.lasx.xvmuh.wu"] - fn __lasx_xvmuh_wu(a: __v8u32, b: __v8u32) -> __v8u32; - #[link_name = "llvm.loongarch.lasx.xvmuh.du"] - fn __lasx_xvmuh_du(a: __v4u64, b: __v4u64) -> __v4u64; #[link_name = "llvm.loongarch.lasx.xvsllwil.h.b"] fn __lasx_xvsllwil_h_b(a: __v32i8, b: u32) -> __v16i16; #[link_name = "llvm.loongarch.lasx.xvsllwil.w.h"] @@ -443,22 +419,6 @@ unsafe extern "unadjusted" { fn __lasx_xvfrintrne_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvfrintrne.d"] fn __lasx_xvfrintrne_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrintrz.s"] - fn __lasx_xvfrintrz_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrintrz.d"] - fn __lasx_xvfrintrz_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrintrp.s"] - fn __lasx_xvfrintrp_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrintrp.d"] - fn __lasx_xvfrintrp_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrintrm.s"] - fn __lasx_xvfrintrm_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrintrm.d"] - fn __lasx_xvfrintrm_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvld"] - fn __lasx_xvld(a: *const i8, b: i32) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvst"] - fn __lasx_xvst(a: __v32i8, b: *mut i8, c: i32); #[link_name = "llvm.loongarch.lasx.xvstelm.b"] fn __lasx_xvstelm_b(a: __v32i8, b: *mut i8, c: i32, d: u32); #[link_name = "llvm.loongarch.lasx.xvstelm.h"] @@ -489,10 +449,6 @@ unsafe extern "unadjusted" { fn __lasx_xvssrln_w_d(a: __v4i64, b: __v4i64) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvldi"] fn __lasx_xvldi(a: i32) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvldx"] - fn __lasx_xvldx(a: *const i8, b: i64) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvstx"] - fn __lasx_xvstx(a: __v32i8, b: *mut i8, c: i64); #[link_name = "llvm.loongarch.lasx.xvextl.qu.du"] fn __lasx_xvextl_qu_du(a: __v4u64) -> __v4u64; #[link_name = "llvm.loongarch.lasx.vext2xv.h.b"] @@ -709,14 +665,6 @@ unsafe extern "unadjusted" { fn __lasx_xvmaddwod_w_hu_h(a: __v8i32, b: __v16u16, c: __v16i16) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvmaddwod.h.bu.b"] fn __lasx_xvmaddwod_h_bu_b(a: __v16i16, b: __v32u8, c: __v32i8) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrotr.b"] - fn __lasx_xvrotr_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvrotr.h"] - fn __lasx_xvrotr_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrotr.w"] - fn __lasx_xvrotr_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvrotr.d"] - fn __lasx_xvrotr_d(a: __v4i64, b: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvadd.q"] fn __lasx_xvadd_q(a: __v4i64, b: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvsub.q"] @@ -749,14 +697,6 @@ unsafe extern "unadjusted" { fn __lasx_xvexth_du_wu(a: __v8u32) -> __v4u64; #[link_name = "llvm.loongarch.lasx.xvexth.qu.du"] fn __lasx_xvexth_qu_du(a: __v4u64) -> __v4u64; - #[link_name = "llvm.loongarch.lasx.xvrotri.b"] - fn __lasx_xvrotri_b(a: __v32i8, b: u32) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvrotri.h"] - fn __lasx_xvrotri_h(a: __v16i16, b: u32) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrotri.w"] - fn __lasx_xvrotri_w(a: __v8i32, b: u32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvrotri.d"] - fn __lasx_xvrotri_d(a: __v4i64, b: u32) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvextl.q.d"] fn __lasx_xvextl_q_d(a: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvsrlni.b.h"] @@ -1737,20 +1677,6 @@ pub fn lasx_xvfclass_d(a: m256d) -> m256i { unsafe { transmute(__lasx_xvfclass_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrecip_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrecip_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrecip_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrecip_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lasx,frecipe")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1793,20 +1719,6 @@ pub fn lasx_xvfrint_d(a: m256d) -> m256d { unsafe { transmute(__lasx_xvfrint_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrsqrt_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrsqrt_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrsqrt_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrsqrt_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1970,62 +1882,6 @@ pub fn lasx_xvpermi_w(a: m256i, b: m256i) -> m256i { unsafe { transmute(__lasx_xvpermi_w(transmute(a), transmute(b), IMM8)) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_bu(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_bu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_hu(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_hu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_wu(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_wu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_du(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_du(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lasx")] #[rustc_legacy_const_generics(1)] @@ -2623,66 +2479,6 @@ pub fn lasx_xvfrintrne_d(a: m256d) -> m256d { unsafe { transmute(__lasx_xvfrintrne_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrz_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrintrz_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrz_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrintrz_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrp_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrintrp_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrp_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrintrp_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrm_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrintrm_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrm_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrintrm_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvld(mem_addr: *const i8) -> m256i { - static_assert_simm_bits!(IMM_S12, 12); - transmute(__lasx_xvld(mem_addr, IMM_S12)) -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(2)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvst(a: m256i, mem_addr: *mut i8) { - static_assert_simm_bits!(IMM_S12, 12); - __lasx_xvst(transmute(a), mem_addr, IMM_S12) -} - #[inline] #[target_feature(enable = "lasx")] #[rustc_legacy_const_generics(2, 3)] @@ -2810,20 +2606,6 @@ pub fn lasx_xvldi() -> m256i { unsafe { transmute(__lasx_xvldi(IMM_S13)) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvldx(mem_addr: *const i8, b: i64) -> m256i { - transmute(__lasx_xvldx(mem_addr, transmute(b))) -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvstx(a: m256i, mem_addr: *mut i8, b: i64) { - __lasx_xvstx(transmute(a), mem_addr, transmute(b)) -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3592,34 +3374,6 @@ pub fn lasx_xvmaddwod_h_bu_b(a: m256i, b: m256i, c: m256i) -> m256i { unsafe { transmute(__lasx_xvmaddwod_h_bu_b(transmute(a), transmute(b), transmute(c))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3732,42 +3486,6 @@ pub fn lasx_xvexth_qu_du(a: m256i) -> m256i { unsafe { transmute(__lasx_xvexth_qu_du(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_b(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lasx_xvrotri_b(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_h(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lasx_xvrotri_h(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_w(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM5, 5); - unsafe { transmute(__lasx_xvrotri_w(transmute(a), IMM5)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_d(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM6, 6); - unsafe { transmute(__lasx_xvrotri_d(transmute(a), IMM6)) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs index 4dfe80795f68c..110a98cfb06b4 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs @@ -328,6 +328,16 @@ impl_vv!("lasx", lasx_xvneg_w, is::simd_neg, m256i, i32x8); impl_vv!("lasx", lasx_xvneg_d, is::simd_neg, m256i, i64x4); impl_vv!("lasx", lasx_xvfsqrt_s, is::simd_fsqrt, m256, f32x8); impl_vv!("lasx", lasx_xvfsqrt_d, is::simd_fsqrt, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrsqrt_s, ls::simd_frsqrt_s, m256, f32x8); +impl_vv!("lasx", lasx_xvfrsqrt_d, ls::simd_frsqrt_d, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrecip_s, ls::simd_frecip_s, m256, f32x8); +impl_vv!("lasx", lasx_xvfrecip_d, ls::simd_frecip_d, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrintrp_s, is::simd_ceil, m256, f32x8); +impl_vv!("lasx", lasx_xvfrintrp_d, is::simd_ceil, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrintrm_s, is::simd_floor, m256, f32x8); +impl_vv!("lasx", lasx_xvfrintrm_d, is::simd_floor, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrintrz_s, is::simd_trunc, m256, f32x8); +impl_vv!("lasx", lasx_xvfrintrz_d, is::simd_trunc, m256d, f64x4); impl_vv!("lasx", lasx_xvreplve0_b, simd_replve0_b, m256i, i8x32); impl_vv!("lasx", lasx_xvreplve0_h, simd_replve0_h, m256i, i16x16); impl_vv!("lasx", lasx_xvreplve0_w, simd_replve0_w, m256i, i32x8); @@ -339,6 +349,10 @@ impl_gv!("lasx", lasx_xvreplgr2vr_h, ls::simd_splat, m256i, i16x16, i32); impl_gv!("lasx", lasx_xvreplgr2vr_w, ls::simd_splat, m256i, i32x8, i32); impl_gv!("lasx", lasx_xvreplgr2vr_d, ls::simd_splat, m256i, i64x4, i64); +impl_ggv!("lasx", lasx_xvldx, simd_ldx, m256i, i8x32, *const i8, i64, unsafe); + +impl_gsv!("lasx", lasx_xvld, simd_ld, m256i, i8x32, *const i8, 12, const, unsafe); + impl_sv!("lasx", lasx_xvrepli_b, ls::simd_splat, m256i, i8x32, 10); impl_sv!("lasx", lasx_xvrepli_h, ls::simd_splat, m256i, i16x16, 10); impl_sv!("lasx", lasx_xvrepli_w, ls::simd_splat, m256i, i32x8, 10); @@ -434,6 +448,10 @@ impl_vvv!("lasx", lasx_xvsrl_b, ls::simd_shr, m256i, u8x32); impl_vvv!("lasx", lasx_xvsrl_h, ls::simd_shr, m256i, u16x16); impl_vvv!("lasx", lasx_xvsrl_w, ls::simd_shr, m256i, u32x8); impl_vvv!("lasx", lasx_xvsrl_d, ls::simd_shr, m256i, u64x4); +impl_vvv!("lasx", lasx_xvrotr_b, ls::simd_rotr, m256i, u8x32); +impl_vvv!("lasx", lasx_xvrotr_h, ls::simd_rotr, m256i, u16x16); +impl_vvv!("lasx", lasx_xvrotr_w, ls::simd_rotr, m256i, u32x8); +impl_vvv!("lasx", lasx_xvrotr_d, ls::simd_rotr, m256i, u64x4); impl_vvv!("lasx", lasx_xvbitclr_b, ls::simd_bitclr, m256i, u8x32); impl_vvv!("lasx", lasx_xvbitclr_h, ls::simd_bitclr, m256i, u16x16); impl_vvv!("lasx", lasx_xvbitclr_w, ls::simd_bitclr, m256i, u32x8); @@ -474,6 +492,14 @@ impl_vvv!("lasx", lasx_xvabsd_bu, ls::simd_absd, m256i, u8x32); impl_vvv!("lasx", lasx_xvabsd_hu, ls::simd_absd, m256i, u16x16); impl_vvv!("lasx", lasx_xvabsd_wu, ls::simd_absd, m256i, u32x8); impl_vvv!("lasx", lasx_xvabsd_du, ls::simd_absd, m256i, u64x4); +impl_vvv!("lasx", lasx_xvmuh_b, simd_muh, m256i, i8x32, i16x32); +impl_vvv!("lasx", lasx_xvmuh_h, simd_muh, m256i, i16x16, i32x16); +impl_vvv!("lasx", lasx_xvmuh_w, simd_muh, m256i, i32x8, i64x8); +impl_vvv!("lasx", lasx_xvmuh_d, simd_muh, m256i, i64x4, i128x4); +impl_vvv!("lasx", lasx_xvmuh_bu, simd_muh, m256i, u8x32, u16x32); +impl_vvv!("lasx", lasx_xvmuh_hu, simd_muh, m256i, u16x16, u32x16); +impl_vvv!("lasx", lasx_xvmuh_wu, simd_muh, m256i, u32x8, u64x8); +impl_vvv!("lasx", lasx_xvmuh_du, simd_muh, m256i, u64x4, u128x4); impl_vvv!("lasx", lasx_xvpickev_b, simd_pickev_b, m256i, i8x32); impl_vvv!("lasx", lasx_xvpickev_h, simd_pickev_h, m256i, i16x16); impl_vvv!("lasx", lasx_xvpickev_w, simd_pickev_w, m256i, i32x8); @@ -499,6 +525,10 @@ impl_vvv!("lasx", lasx_xvpackod_h, simd_packod_h, m256i, i16x16); impl_vvv!("lasx", lasx_xvpackod_w, simd_packod_w, m256i, i32x8); impl_vvv!("lasx", lasx_xvpackod_d, simd_packod_d, m256i, i64x4); +impl_vgg!("lasx", lasx_xvstx, simd_stx, m256i, i8x32, *mut i8, i64, unsafe); + +impl_vgs!("lasx", lasx_xvst, simd_st, m256i, i8x32, *mut i8, 12, const, unsafe); + impl_vuv!("lasx", lasx_xvslli_b, is::simd_shl, m256i, i8x32); impl_vuv!("lasx", lasx_xvslli_h, is::simd_shl, m256i, i16x16); impl_vuv!("lasx", lasx_xvslli_w, is::simd_shl, m256i, i32x8); @@ -511,6 +541,10 @@ impl_vuv!("lasx", lasx_xvsrli_b, is::simd_shr, m256i, u8x32); impl_vuv!("lasx", lasx_xvsrli_h, is::simd_shr, m256i, u16x16); impl_vuv!("lasx", lasx_xvsrli_w, is::simd_shr, m256i, u32x8); impl_vuv!("lasx", lasx_xvsrli_d, is::simd_shr, m256i, u64x4); +impl_vuv!("lasx", lasx_xvrotri_b, ls::simd_rotr, m256i, u8x32); +impl_vuv!("lasx", lasx_xvrotri_h, ls::simd_rotr, m256i, u16x16); +impl_vuv!("lasx", lasx_xvrotri_w, ls::simd_rotr, m256i, u32x8); +impl_vuv!("lasx", lasx_xvrotri_d, ls::simd_rotr, m256i, u64x4); impl_vuv!("lasx", lasx_xvaddi_bu, is::simd_add, m256i, u8x32, 5); impl_vuv!("lasx", lasx_xvaddi_hu, is::simd_add, m256i, u16x16, 5); impl_vuv!("lasx", lasx_xvaddi_wu, is::simd_add, m256i, u32x8, 5); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs index 555866040e420..6483c6296a344 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs @@ -205,10 +205,6 @@ unsafe extern "unadjusted" { fn __lsx_vfclass_s(a: __v4f32) -> __v4i32; #[link_name = "llvm.loongarch.lsx.vfclass.d"] fn __lsx_vfclass_d(a: __v2f64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vfrecip.s"] - fn __lsx_vfrecip_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrecip.d"] - fn __lsx_vfrecip_d(a: __v2f64) -> __v2f64; #[link_name = "llvm.loongarch.lsx.vfrecipe.s"] fn __lsx_vfrecipe_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vfrecipe.d"] @@ -221,10 +217,6 @@ unsafe extern "unadjusted" { fn __lsx_vfrint_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vfrint.d"] fn __lsx_vfrint_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrsqrt.s"] - fn __lsx_vfrsqrt_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrsqrt.d"] - fn __lsx_vfrsqrt_d(a: __v2f64) -> __v2f64; #[link_name = "llvm.loongarch.lsx.vflogb.s"] fn __lsx_vflogb_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vflogb.d"] @@ -261,22 +253,6 @@ unsafe extern "unadjusted" { fn __lsx_vffint_s_wu(a: __v4u32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vffint.d.lu"] fn __lsx_vffint_d_lu(a: __v2u64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vmuh.b"] - fn __lsx_vmuh_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vmuh.h"] - fn __lsx_vmuh_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vmuh.w"] - fn __lsx_vmuh_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vmuh.d"] - fn __lsx_vmuh_d(a: __v2i64, b: __v2i64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vmuh.bu"] - fn __lsx_vmuh_bu(a: __v16u8, b: __v16u8) -> __v16u8; - #[link_name = "llvm.loongarch.lsx.vmuh.hu"] - fn __lsx_vmuh_hu(a: __v8u16, b: __v8u16) -> __v8u16; - #[link_name = "llvm.loongarch.lsx.vmuh.wu"] - fn __lsx_vmuh_wu(a: __v4u32, b: __v4u32) -> __v4u32; - #[link_name = "llvm.loongarch.lsx.vmuh.du"] - fn __lsx_vmuh_du(a: __v2u64, b: __v2u64) -> __v2u64; #[link_name = "llvm.loongarch.lsx.vsllwil.h.b"] fn __lsx_vsllwil_h_b(a: __v16i8, b: u32) -> __v8i16; #[link_name = "llvm.loongarch.lsx.vsllwil.w.h"] @@ -437,18 +413,6 @@ unsafe extern "unadjusted" { fn __lsx_vfrintrne_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vfrintrne.d"] fn __lsx_vfrintrne_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrintrz.s"] - fn __lsx_vfrintrz_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrintrz.d"] - fn __lsx_vfrintrz_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrintrp.s"] - fn __lsx_vfrintrp_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrintrp.d"] - fn __lsx_vfrintrp_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrintrm.s"] - fn __lsx_vfrintrm_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrintrm.d"] - fn __lsx_vfrintrm_d(a: __v2f64) -> __v2f64; #[link_name = "llvm.loongarch.lsx.vstelm.b"] fn __lsx_vstelm_b(a: __v16i8, b: *mut i8, c: i32, d: u32); #[link_name = "llvm.loongarch.lsx.vstelm.h"] @@ -641,14 +605,6 @@ unsafe extern "unadjusted" { fn __lsx_vmaddwev_q_du_d(a: __v2i64, b: __v2u64, c: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vmaddwod.q.du.d"] fn __lsx_vmaddwod_q_du_d(a: __v2i64, b: __v2u64, c: __v2i64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vrotr.b"] - fn __lsx_vrotr_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vrotr.h"] - fn __lsx_vrotr_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vrotr.w"] - fn __lsx_vrotr_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vrotr.d"] - fn __lsx_vrotr_d(a: __v2i64, b: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vadd.q"] fn __lsx_vadd_q(a: __v2i64, b: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vsub.q"] @@ -681,14 +637,6 @@ unsafe extern "unadjusted" { fn __lsx_vexth_du_wu(a: __v4u32) -> __v2u64; #[link_name = "llvm.loongarch.lsx.vexth.qu.du"] fn __lsx_vexth_qu_du(a: __v2u64) -> __v2u64; - #[link_name = "llvm.loongarch.lsx.vrotri.b"] - fn __lsx_vrotri_b(a: __v16i8, b: u32) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vrotri.h"] - fn __lsx_vrotri_h(a: __v8i16, b: u32) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vrotri.w"] - fn __lsx_vrotri_w(a: __v4i32, b: u32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vrotri.d"] - fn __lsx_vrotri_d(a: __v2i64, b: u32) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vextl.q.d"] fn __lsx_vextl_q_d(a: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vsrlni.b.h"] @@ -789,10 +737,6 @@ unsafe extern "unadjusted" { fn __lsx_vssrarni_du_q(a: __v2u64, b: __v2i64, c: u32) -> __v2u64; #[link_name = "llvm.loongarch.lsx.vpermi.w"] fn __lsx_vpermi_w(a: __v4i32, b: __v4i32, c: u32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vld"] - fn __lsx_vld(a: *const i8, b: i32) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vst"] - fn __lsx_vst(a: __v16i8, b: *mut i8, c: i32); #[link_name = "llvm.loongarch.lsx.vssrlrn.b.h"] fn __lsx_vssrlrn_b_h(a: __v8i16, b: __v8i16) -> __v16i8; #[link_name = "llvm.loongarch.lsx.vssrlrn.h.w"] @@ -809,10 +753,6 @@ unsafe extern "unadjusted" { fn __lsx_vldi(a: i32) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vshuf.b"] fn __lsx_vshuf_b(a: __v16i8, b: __v16i8, c: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vldx"] - fn __lsx_vldx(a: *const i8, b: i64) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vstx"] - fn __lsx_vstx(a: __v16i8, b: *mut i8, c: i64); #[link_name = "llvm.loongarch.lsx.vextl.qu.du"] fn __lsx_vextl_qu_du(a: __v2u64) -> __v2u64; #[link_name = "llvm.loongarch.lsx.bnz.b"] @@ -1678,20 +1618,6 @@ pub fn lsx_vfclass_d(a: m128d) -> m128i { unsafe { transmute(__lsx_vfclass_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrecip_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrecip_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrecip_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrecip_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lsx,frecipe")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1734,20 +1660,6 @@ pub fn lsx_vfrint_d(a: m128d) -> m128d { unsafe { transmute(__lsx_vfrint_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrsqrt_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrsqrt_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrsqrt_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrsqrt_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1874,62 +1786,6 @@ pub fn lsx_vffint_d_lu(a: m128i) -> m128d { unsafe { transmute(__lsx_vffint_d_lu(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_bu(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_bu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_hu(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_hu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_wu(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_wu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_du(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_du(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lsx")] #[rustc_legacy_const_generics(1)] @@ -2518,48 +2374,6 @@ pub fn lsx_vfrintrne_d(a: m128d) -> m128d { unsafe { transmute(__lsx_vfrintrne_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrz_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrintrz_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrz_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrintrz_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrp_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrintrp_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrp_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrintrp_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrm_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrintrm_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrm_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrintrm_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lsx")] #[rustc_legacy_const_generics(2, 3)] @@ -3244,34 +3058,6 @@ pub fn lsx_vmaddwod_q_du_d(a: m128i, b: m128i, c: m128i) -> m128i { unsafe { transmute(__lsx_vmaddwod_q_du_d(transmute(a), transmute(b), transmute(c))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3392,42 +3178,6 @@ pub fn lsx_vexth_qu_du(a: m128i) -> m128i { unsafe { transmute(__lsx_vexth_qu_du(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_b(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lsx_vrotri_b(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_h(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lsx_vrotri_h(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_w(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM5, 5); - unsafe { transmute(__lsx_vrotri_w(transmute(a), IMM5)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_d(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM6, 6); - unsafe { transmute(__lsx_vrotri_d(transmute(a), IMM6)) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3876,24 +3626,6 @@ pub fn lsx_vpermi_w(a: m128i, b: m128i) -> m128i { unsafe { transmute(__lsx_vpermi_w(transmute(a), transmute(b), IMM8)) } } -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vld(mem_addr: *const i8) -> m128i { - static_assert_simm_bits!(IMM_S12, 12); - transmute(__lsx_vld(mem_addr, IMM_S12)) -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(2)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vst(a: m128i, mem_addr: *mut i8) { - static_assert_simm_bits!(IMM_S12, 12); - __lsx_vst(transmute(a), mem_addr, IMM_S12) -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3952,20 +3684,6 @@ pub fn lsx_vshuf_b(a: m128i, b: m128i, c: m128i) -> m128i { unsafe { transmute(__lsx_vshuf_b(transmute(a), transmute(b), transmute(c))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vldx(mem_addr: *const i8, b: i64) -> m128i { - transmute(__lsx_vldx(mem_addr, transmute(b))) -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vstx(a: m128i, mem_addr: *mut i8, b: i64) { - __lsx_vstx(transmute(a), mem_addr, transmute(b)) -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs index 0b0df11bbfaac..2ceed73283726 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs @@ -227,12 +227,26 @@ impl_vv!("lsx", lsx_vneg_w, is::simd_neg, m128i, i32x4); impl_vv!("lsx", lsx_vneg_d, is::simd_neg, m128i, i64x2); impl_vv!("lsx", lsx_vfsqrt_s, is::simd_fsqrt, m128, f32x4); impl_vv!("lsx", lsx_vfsqrt_d, is::simd_fsqrt, m128d, f64x2); +impl_vv!("lsx", lsx_vfrsqrt_s, ls::simd_frsqrt_s, m128, f32x4); +impl_vv!("lsx", lsx_vfrsqrt_d, ls::simd_frsqrt_d, m128d, f64x2); +impl_vv!("lsx", lsx_vfrecip_s, ls::simd_frecip_s, m128, f32x4); +impl_vv!("lsx", lsx_vfrecip_d, ls::simd_frecip_d, m128d, f64x2); +impl_vv!("lsx", lsx_vfrintrp_s, is::simd_ceil, m128, f32x4); +impl_vv!("lsx", lsx_vfrintrp_d, is::simd_ceil, m128d, f64x2); +impl_vv!("lsx", lsx_vfrintrm_s, is::simd_floor, m128, f32x4); +impl_vv!("lsx", lsx_vfrintrm_d, is::simd_floor, m128d, f64x2); +impl_vv!("lsx", lsx_vfrintrz_s, is::simd_trunc, m128, f32x4); +impl_vv!("lsx", lsx_vfrintrz_d, is::simd_trunc, m128d, f64x2); impl_gv!("lsx", lsx_vreplgr2vr_b, ls::simd_splat, m128i, i8x16, i32); impl_gv!("lsx", lsx_vreplgr2vr_h, ls::simd_splat, m128i, i16x8, i32); impl_gv!("lsx", lsx_vreplgr2vr_w, ls::simd_splat, m128i, i32x4, i32); impl_gv!("lsx", lsx_vreplgr2vr_d, ls::simd_splat, m128i, i64x2, i64); +impl_ggv!("lsx", lsx_vldx, simd_ldx, m128i, i8x16, *const i8, i64, unsafe); + +impl_gsv!("lsx", lsx_vld, simd_ld, m128i, i8x16, *const i8, 12, const, unsafe); + impl_sv!("lsx", lsx_vrepli_b, ls::simd_splat, m128i, i8x16, 10); impl_sv!("lsx", lsx_vrepli_h, ls::simd_splat, m128i, i16x8, 10); impl_sv!("lsx", lsx_vrepli_w, ls::simd_splat, m128i, i32x4, 10); @@ -328,6 +342,10 @@ impl_vvv!("lsx", lsx_vsrl_b, ls::simd_shr, m128i, u8x16); impl_vvv!("lsx", lsx_vsrl_h, ls::simd_shr, m128i, u16x8); impl_vvv!("lsx", lsx_vsrl_w, ls::simd_shr, m128i, u32x4); impl_vvv!("lsx", lsx_vsrl_d, ls::simd_shr, m128i, u64x2); +impl_vvv!("lsx", lsx_vrotr_b, ls::simd_rotr, m128i, u8x16); +impl_vvv!("lsx", lsx_vrotr_h, ls::simd_rotr, m128i, u16x8); +impl_vvv!("lsx", lsx_vrotr_w, ls::simd_rotr, m128i, u32x4); +impl_vvv!("lsx", lsx_vrotr_d, ls::simd_rotr, m128i, u64x2); impl_vvv!("lsx", lsx_vbitclr_b, ls::simd_bitclr, m128i, u8x16); impl_vvv!("lsx", lsx_vbitclr_h, ls::simd_bitclr, m128i, u16x8); impl_vvv!("lsx", lsx_vbitclr_w, ls::simd_bitclr, m128i, u32x4); @@ -368,6 +386,14 @@ impl_vvv!("lsx", lsx_vabsd_bu, ls::simd_absd, m128i, u8x16); impl_vvv!("lsx", lsx_vabsd_hu, ls::simd_absd, m128i, u16x8); impl_vvv!("lsx", lsx_vabsd_wu, ls::simd_absd, m128i, u32x4); impl_vvv!("lsx", lsx_vabsd_du, ls::simd_absd, m128i, u64x2); +impl_vvv!("lsx", lsx_vmuh_b, simd_muh, m128i, i8x16, i16x16); +impl_vvv!("lsx", lsx_vmuh_h, simd_muh, m128i, i16x8, i32x8); +impl_vvv!("lsx", lsx_vmuh_w, simd_muh, m128i, i32x4, i64x4); +impl_vvv!("lsx", lsx_vmuh_d, simd_muh, m128i, i64x2, i128x2); +impl_vvv!("lsx", lsx_vmuh_bu, simd_muh, m128i, u8x16, u16x16); +impl_vvv!("lsx", lsx_vmuh_hu, simd_muh, m128i, u16x8, u32x8); +impl_vvv!("lsx", lsx_vmuh_wu, simd_muh, m128i, u32x4, u64x4); +impl_vvv!("lsx", lsx_vmuh_du, simd_muh, m128i, u64x2, u128x2); impl_vvv!("lsx", lsx_vpickev_b, simd_pickev_b, m128i, i8x16); impl_vvv!("lsx", lsx_vpickev_h, simd_pickev_h, m128i, i16x8); impl_vvv!("lsx", lsx_vpickev_w, simd_pickev_w, m128i, i32x4); @@ -393,6 +419,10 @@ impl_vvv!("lsx", lsx_vpackod_h, simd_packod_h, m128i, i16x8); impl_vvv!("lsx", lsx_vpackod_w, simd_packod_w, m128i, i32x4); impl_vvv!("lsx", lsx_vpackod_d, simd_packod_d, m128i, i64x2); +impl_vgg!("lsx", lsx_vstx, simd_stx, m128i, i8x16, *mut i8, i64, unsafe); + +impl_vgs!("lsx", lsx_vst, simd_st, m128i, i8x16, *mut i8, 12, const, unsafe); + impl_vuv!("lsx", lsx_vslli_b, is::simd_shl, m128i, i8x16); impl_vuv!("lsx", lsx_vslli_h, is::simd_shl, m128i, i16x8); impl_vuv!("lsx", lsx_vslli_w, is::simd_shl, m128i, i32x4); @@ -405,6 +435,10 @@ impl_vuv!("lsx", lsx_vsrli_b, is::simd_shr, m128i, u8x16); impl_vuv!("lsx", lsx_vsrli_h, is::simd_shr, m128i, u16x8); impl_vuv!("lsx", lsx_vsrli_w, is::simd_shr, m128i, u32x4); impl_vuv!("lsx", lsx_vsrli_d, is::simd_shr, m128i, u64x2); +impl_vuv!("lsx", lsx_vrotri_b, ls::simd_rotr, m128i, u8x16); +impl_vuv!("lsx", lsx_vrotri_h, ls::simd_rotr, m128i, u16x8); +impl_vuv!("lsx", lsx_vrotri_w, ls::simd_rotr, m128i, u32x4); +impl_vuv!("lsx", lsx_vrotri_d, ls::simd_rotr, m128i, u64x2); impl_vuv!("lsx", lsx_vaddi_bu, is::simd_add, m128i, u8x16, 5); impl_vuv!("lsx", lsx_vaddi_hu, is::simd_add, m128i, u16x8, 5); impl_vuv!("lsx", lsx_vaddi_wu, is::simd_add, m128i, u32x4, 5); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs index 32f191509be27..58dd016400432 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs @@ -35,16 +35,26 @@ impl_simd_ext!(u8x16, u8); impl_simd_ext!(u8x32, u8); impl_simd_ext!(i16x8, i16); impl_simd_ext!(i16x16, i16); +impl_simd_ext!(i16x32, i16); impl_simd_ext!(u16x8, u16); impl_simd_ext!(u16x16, u16); +impl_simd_ext!(u16x32, u16); impl_simd_ext!(i32x4, i32); impl_simd_ext!(i32x8, i32); +impl_simd_ext!(i32x16, i32); impl_simd_ext!(u32x4, u32); impl_simd_ext!(u32x8, u32); +impl_simd_ext!(u32x16, u32); impl_simd_ext!(i64x2, i64); impl_simd_ext!(i64x4, i64); +impl_simd_ext!(i64x8, i64); impl_simd_ext!(u64x2, u64); impl_simd_ext!(u64x4, u64); +impl_simd_ext!(u64x8, u64); +impl_simd_ext!(i128x2, i128); +impl_simd_ext!(u128x2, u128); +impl_simd_ext!(i128x4, i128); +impl_simd_ext!(u128x4, u128); #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] @@ -108,6 +118,42 @@ pub(super) const unsafe fn simd_fnmsub(a: T, b: T, c: T) -> T { is::simd_neg(ls::simd_fmsub(a, b, c)) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_frecip_s(a: T) -> T { + is::simd_div(is::simd_splat(1.0f32), a) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_frecip_d(a: T) -> T { + is::simd_div(is::simd_splat(1.0f64), a) +} + +#[inline(always)] +pub(super) unsafe fn simd_frsqrt_s(a: T) -> T { + ls::simd_frecip_s(is::simd_fsqrt(a)) +} + +#[inline(always)] +pub(super) unsafe fn simd_frsqrt_d(a: T) -> T { + ls::simd_frecip_d(is::simd_fsqrt(a)) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_ld(a: *const i8) -> T { + let a = a.offset(I as isize) as *const T; + core::ptr::read_unaligned(a) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_ldx(a: *const i8, b: i64) -> T { + let a = a.offset(b as isize) as *const T; + core::ptr::read_unaligned(a) +} + #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] pub(super) const unsafe fn simd_madd(a: T, b: T, c: T) -> T { @@ -120,6 +166,18 @@ pub(super) const unsafe fn simd_msub(a: T, b: T, c: T) -> T { is::simd_sub(a, is::simd_mul(b, c)) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_muh(a: T, b: T) -> T { + let a: W = is::simd_cast(a); + let b: W = is::simd_cast(b); + let p = is::simd_mul(a, b); + is::simd_cast(is::simd_shr( + p, + ls::simd_splat((size_of::() * 8 / 2) as i64), + )) +} + #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] pub(super) const unsafe fn simd_nor(a: T, b: T) -> T { @@ -138,6 +196,15 @@ pub(super) const unsafe fn simd_orn(a: T, b: T) -> T { is::simd_or(a, ls::simd_not(b)) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_rotr(a: T, b: T) -> T { + let m = (size_of::() * 8 - 1) as i64; + let r = is::simd_and(b, ls::simd_splat(m)); + let l = is::simd_and(is::simd_sub(ls::simd_splat(m + 1), r), ls::simd_splat(m)); + is::simd_or(is::simd_shr(a, r), is::simd_shl(a, l)) +} + #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] pub(super) const unsafe fn simd_shl(a: T, b: T) -> T { @@ -158,6 +225,20 @@ pub(super) const unsafe fn simd_splat(a: i64) -> T { T::splat(a) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_st(a: T, b: *mut i8) { + let b = b.offset(I as isize) as *mut T; + core::ptr::write_unaligned(b, a); +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_stx(a: T, b: *mut i8, c: i64) { + let b = b.offset(c as isize) as *mut T; + core::ptr::write_unaligned(b, a); +} + macro_rules! impl_vv { ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ty) => { #[inline] @@ -191,6 +272,36 @@ macro_rules! impl_gv { pub(super) use impl_gv; +macro_rules! impl_ggv { + ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident, $gty:ty, $xty:ty, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $gty, b: $xty) -> $oty { + let r: $ity = $op(a, b); + transmute(r) + } + }; +} + +pub(super) use impl_ggv; + +macro_rules! impl_gsv { + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ident, $gty:ty, $ibs:expr, const, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[rustc_legacy_const_generics(1)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $gty) -> $oty { + static_assert_simm_bits!(IMM, $ibs); + let r: $ity = $op::(a); + transmute(r) + } + }; +} + +pub(super) use impl_gsv; + macro_rules! impl_sv { ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident, $ibs:expr) => { #[inline] @@ -223,10 +334,51 @@ macro_rules! impl_vvv { } } }; + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ty, $wty:ty) => { + #[inline] + #[target_feature(enable = $ft)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub fn $name(a: $oty, b: $oty) -> $oty { + unsafe { + let a: $ity = transmute(a); + let b: $ity = transmute(b); + let r: $ity = $op::<$ity, $wty>(a, b); + transmute(r) + } + } + }; } pub(super) use impl_vvv; +macro_rules! impl_vgg { + ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident, $gty:ty, $xty:ty, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $oty, b: $gty, c: $xty) { + $op(a, b, c); + } + }; +} + +pub(super) use impl_vgg; + +macro_rules! impl_vgs { + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ident, $gty:ty, $ibs:expr, const, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[rustc_legacy_const_generics(2)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $oty, b: $gty) { + static_assert_simm_bits!(IMM, $ibs); + $op::(a, b); + } + }; +} + +pub(super) use impl_vgs; + macro_rules! impl_vuv { ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident) => { #[inline] diff --git a/library/stdarch/crates/core_arch/src/simd.rs b/library/stdarch/crates/core_arch/src/simd.rs index 30c3125f5f8b8..08ee17548a01d 100644 --- a/library/stdarch/crates/core_arch/src/simd.rs +++ b/library/stdarch/crates/core_arch/src/simd.rs @@ -28,11 +28,13 @@ unsafe impl SimdElement for u8 {} unsafe impl SimdElement for u16 {} unsafe impl SimdElement for u32 {} unsafe impl SimdElement for u64 {} +unsafe impl SimdElement for u128 {} unsafe impl SimdElement for i8 {} unsafe impl SimdElement for i16 {} unsafe impl SimdElement for i32 {} unsafe impl SimdElement for i64 {} +unsafe impl SimdElement for i128 {} unsafe impl SimdElement for f16 {} unsafe impl SimdElement for f32 {} @@ -371,11 +373,13 @@ pub(crate) type u8x32 = Simd; pub(crate) type u16x16 = Simd; pub(crate) type u32x8 = Simd; pub(crate) type u64x4 = Simd; +pub(crate) type u128x2 = Simd; pub(crate) type i8x32 = Simd; pub(crate) type i16x16 = Simd; pub(crate) type i32x8 = Simd; pub(crate) type i64x4 = Simd; +pub(crate) type i128x2 = Simd; pub(crate) type f16x16 = Simd; pub(crate) type f32x8 = Simd; @@ -391,11 +395,13 @@ pub(crate) type u8x64 = Simd; pub(crate) type u16x32 = Simd; pub(crate) type u32x16 = Simd; pub(crate) type u64x8 = Simd; +pub(crate) type u128x4 = Simd; pub(crate) type i8x64 = Simd; pub(crate) type i16x32 = Simd; pub(crate) type i32x16 = Simd; pub(crate) type i64x8 = Simd; +pub(crate) type i128x4 = Simd; pub(crate) type f16x32 = Simd; pub(crate) type f32x16 = Simd; diff --git a/library/stdarch/crates/core_arch/src/x86/sse2.rs b/library/stdarch/crates/core_arch/src/x86/sse2.rs index bd7e4df2138d5..5e8fe959c301a 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse2.rs @@ -1445,7 +1445,7 @@ pub unsafe fn _mm_stream_si128(mem_addr: *mut __m128i, a: __m128i) { ); } -/// Stores a 32-bit integer value in the specified memory location. +/// Stores a 32-bit integer value in a 4-byte aligned memory location. /// To minimize caching, the data is flagged as non-temporal (unlikely to be /// used again soon). /// diff --git a/library/stdarch/crates/core_arch/src/x86_64/amx.rs b/library/stdarch/crates/core_arch/src/x86_64/amx.rs index cddae18b7684d..f97acf1d3f746 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/amx.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/amx.rs @@ -632,50 +632,6 @@ pub unsafe fn __tile_stream_loaddrs(dst: *mut __tile1024i, base: *const u8, stri (*dst).tile = tileloaddrst164_internal((*dst).rows, (*dst).colsb, base, stride as u64); } -/// Perform matrix multiplication of two tiles a and b, containing packed single precision (32-bit) -/// floating-point elements, which are converted to TF32 (tensor-float32) format, and accumulate the -/// results into a packed single precision tile. -/// For each possible combination of (row of a, column of b), it performs -/// - convert to TF32 -/// - multiply the corresponding elements of a and b -/// - accumulate the results into the corresponding row and column of dst using round-to-nearest-even -/// rounding mode. -/// Output FP32 denormals are always flushed to zero, input single precision denormals are always -/// handled and *not* treated as zero. -#[inline] -#[rustc_legacy_const_generics(0, 1, 2)] -#[target_feature(enable = "amx-tf32")] -#[cfg_attr( - all(test, not(target_vendor = "apple")), - assert_instr(tmmultf32ps, DST = 0, A = 1, B = 2) -)] -#[unstable(feature = "x86_amx_intrinsics", issue = "126622")] -pub unsafe fn _tile_mmultf32ps() { - static_assert_uimm_bits!(DST, 3); - static_assert_uimm_bits!(A, 3); - static_assert_uimm_bits!(B, 3); - tmmultf32ps(DST as i8, A as i8, B as i8); -} - -/// Perform matrix multiplication of two tiles a and b, containing packed single precision (32-bit) -/// floating-point elements, which are converted to TF32 (tensor-float32) format, and accumulate the -/// results into a packed single precision tile. -/// For each possible combination of (row of a, column of b), it performs -/// - convert to TF32 -/// - multiply the corresponding elements of a and b -/// - accumulate the results into the corresponding row and column of dst using round-to-nearest-even -/// rounding mode. -/// Output FP32 denormals are always flushed to zero, input single precision denormals are always -/// handled and *not* treated as zero. -/// The shape of the tile is specified in the struct of [`__tile1024i`]. The register of the tile is allocated by the compiler. -#[inline] -#[target_feature(enable = "amx-tf32")] -#[cfg_attr(all(test, not(target_vendor = "apple")), assert_instr(tmmultf32ps))] -#[unstable(feature = "x86_amx_intrinsics", issue = "126622")] -pub unsafe fn __tile_mmultf32ps(dst: *mut __tile1024i, a: __tile1024i, b: __tile1024i) { - (*dst).tile = tmmultf32ps_internal(a.rows, b.colsb, a.colsb, (*dst).tile, a.tile, b.tile); -} - /// Moves a row from a tile register to a zmm register, converting the packed 32-bit signed integer /// elements to packed single-precision (32-bit) floating-point elements. #[inline] @@ -1037,11 +993,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.x86.tileloaddrst164.internal"] fn tileloaddrst164_internal(rows: u16, colsb: u16, base: *const u8, stride: u64) -> Tile; - #[link_name = "llvm.x86.tmmultf32ps"] - fn tmmultf32ps(dst: i8, a: i8, b: i8); - #[link_name = "llvm.x86.tmmultf32ps.internal"] - fn tmmultf32ps_internal(m: u16, n: u16, k: u16, dst: Tile, a: Tile, b: Tile) -> Tile; - #[link_name = "llvm.x86.tcvtrowd2ps"] fn tcvtrowd2ps(tile: i8, row: u32) -> f32x16; #[link_name = "llvm.x86.tcvtrowd2psi"] @@ -2368,53 +2319,4 @@ mod tests { } } } - - #[simd_test(enable = "amx-tf32")] - fn test_tile_mmultf32ps() { - unsafe { - _init_amx(); - let a: [[f32; 16]; 16] = array::from_fn(|i| [i as _; _]); - let b: [[f32; 16]; 16] = [array::from_fn(|j| j as _); _]; - let mut res = [[0.0; 16]; 16]; - - let mut config = __tilecfg::default(); - config.palette = 1; - (0..=2).for_each(|i| { - config.colsb[i] = 64; - config.rows[i] = 16; - }); - _tile_loadconfig(config.as_ptr()); - _tile_zero::<0>(); - _tile_loadd::<1>(a.as_ptr().cast(), 64); - _tile_loadd::<2>(b.as_ptr().cast(), 64); - _tile_mmultf32ps::<0, 1, 2>(); - _tile_stored::<0>(res.as_mut_ptr().cast(), 64); - _tile_release(); - - let expected = array::from_fn(|i| array::from_fn(|j| 16.0 * i as f32 * j as f32)); - assert_eq!(res, expected); - } - } - - #[simd_test(enable = "amx-tf32")] - fn test__tile_mmultf32ps() { - unsafe { - _init_amx(); - let a: [[f32; 16]; 16] = array::from_fn(|i| [i as _; _]); - let b: [[f32; 16]; 16] = [array::from_fn(|j| j as _); _]; - let mut res = [[0.0; 16]; 16]; - - let mut tile_a = __tile1024i::zeroed(16, 64); - let mut tile_b = __tile1024i::zeroed(16, 64); - let mut tile_c = __tile1024i::zeroed(16, 64); - - __tile_loadd(&mut tile_a, a.as_ptr().cast(), 64); - __tile_loadd(&mut tile_b, b.as_ptr().cast(), 64); - __tile_mmultf32ps(&mut tile_c, tile_a, tile_b); - __tile_stored(res.as_mut_ptr().cast(), 64, tile_c); - - let expected = array::from_fn(|i| array::from_fn(|j| 16.0 * i as f32 * j as f32)); - assert_eq!(res, expected); - } - } } diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs index c4768cedbfa6b..ba1d811fe5b20 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs @@ -59,7 +59,7 @@ pub fn _mm_cvttsd_si64x(a: __m128d) -> i64 { _mm_cvttsd_si64(a) } -/// Stores a 64-bit integer value in the specified memory location. +/// Stores a 64-bit integer value in an 8-byte aligned memory location. /// To minimize caching, the data is flagged as non-temporal (unlikely to be /// used again soon). /// diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 26f861ca64b62..013c20f2db594 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -58,22 +58,14 @@ struct JsonIntrinsic { _instructions: Option>>, } -pub fn get_neon_intrinsics( - filename: &Path, -) -> Result>, Box> { +pub fn get_intrinsics(filename: &Path) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let json: Vec = serde_json::from_reader(reader).expect("Couldn't parse JSON"); let parsed = json .into_iter() - .filter_map(|intr| { - if intr.simd_isa == "Neon" { - Some(json_to_intrinsic(intr).expect("Couldn't parse JSON")) - } else { - None - } - }) + .map(|intr| json_to_intrinsic(intr).expect("Couldn't parse JSON")) .collect(); Ok(parsed) } @@ -121,8 +113,14 @@ fn json_to_intrinsic( } }); - let mut arg = - Argument::::new(i, String::from(arg_name), ArmType(arg_ty), constraint); + let is_predicate = arg_name == "pg"; + let mut arg = Argument::::new( + i, + String::from(arg_name), + ArmType(arg_ty), + constraint, + is_predicate, + ); // The JSON doesn't list immediates as const let IntrinsicType { diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index a60e0ff1551a3..4054d02502bfa 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -2,12 +2,14 @@ mod intrinsic; mod json_parser; mod types; -use crate::common::SupportedArchitecture; +use crate::common::argument::Argument; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::{SimdLen, TypeKind}; +use crate::common::intrinsic_helpers::{SimdLen, TypeDefinition, TypeKind}; +use crate::common::values::test_values_array_name; +use crate::common::{PASSES, PREDICATE_LOCAL, SupportedArchitecture}; use intrinsic::ArmType; -use json_parser::get_neon_intrinsics; +use json_parser::get_intrinsics; #[derive(PartialEq)] pub struct Arm(Vec>); @@ -29,12 +31,21 @@ impl SupportedArchitecture for Arm { #include #include #include +#ifdef __ARM_FEATURE_SVE +#include +#endif "#; const RUST_PRELUDE: &str = RUST_PRELUDE; fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { // GCC uses an extra `-` in the arch name + let big_endian = cli_options.target.starts_with("aarch64_be"); + let a32 = cli_options.target.starts_with("armv7"); match cli_options.cc_arg_style { + CcArgStyle::Clang if !a32 && !big_endian => vec![ + "-march=armv8.6a+crypto+crc+dotprod+fp16+sve2-aes+sve2-sm4+sve2-sha3+sve2-bitperm+\ + f32mm+f64mm+sve2p1", + ], CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], // SVE tests aren't run under GCC so there are no target features added for SVE CcArgStyle::Gcc => vec!["-march=armv8.6-a+crypto+crc+dotprod+fp16+sha3+sm4"], @@ -45,7 +56,7 @@ impl SupportedArchitecture for Arm { let big_endian = cli_options.target.starts_with("aarch64_be"); let a32 = cli_options.target.starts_with("armv7"); let mut intrinsics = - get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); + get_intrinsics(&cli_options.filename).expect("Error parsing input file"); intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); intrinsics.dedup(); @@ -69,13 +80,13 @@ impl SupportedArchitecture for Arm { let has_sve_arg = i .arguments .iter() - .any(|a| a.ty.simd_len == Some(SimdLen::Scalable)); + .any(|a| a.ty.num_lanes() == SimdLen::Scalable); !(has_f16_arg && has_sve_arg) }) .filter(|i| { let has_f16_ret = i.results.kind() == TypeKind::Float && i.results.bit_len == Some(16); - let has_sve_ret = i.results.simd_len == Some(SimdLen::Scalable); + let has_sve_ret = i.results.num_lanes() == SimdLen::Scalable; !(has_f16_ret && has_sve_ret) }) // Skip `svqcvtn{u,}n*_x2` intrinsics - not yet implemented! @@ -129,6 +140,35 @@ impl SupportedArchitecture for Arm { Self(intrinsics) } + + fn predicate_function(size: u32) -> String { + format!("svptrue_b{size}()") + } + + fn load_call(arg: &Argument, idx: usize) -> String { + let name = arg.generate_name(); + let load = arg.ty.load_function(); + let ptr = format!( + "{vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _", + vals_name = test_values_array_name(&arg.ty) + ); + + match arg.ty.num_lanes() { + // If the load is of a `svbool_t`, then we load a `svint8_t` and + SimdLen::Scalable if matches!(arg.ty.kind(), TypeKind::Bool) => { + format!( + r#" +let {name} = {load}({PREDICATE_LOCAL}, {ptr}); +let {name} = svcmpne_n_s8({PREDICATE_LOCAL}, {name}, 0); + "# + ) + } + // If this load is of a scalable vector, then prepend an additional argument + // containing the predicate for the load. + SimdLen::Scalable => format!("let {name} = {load}({PREDICATE_LOCAL}, {ptr});"), + SimdLen::Fixed(..) => format!("let {name} = {load}({ptr});"), + } + } } const RUST_PRELUDE: &str = r#" @@ -140,6 +180,7 @@ const RUST_PRELUDE: &str = r#" #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] +#![cfg_attr(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"), feature(stdarch_aarch64_sve))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] #![feature(stdarch_neon_f16)] @@ -148,4 +189,116 @@ use core_arch::arch::aarch64::*; #[cfg(target_arch = "arm")] use core_arch::arch::arm::*; + +#[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] +const fn svpattern_from_i32(value: i32) -> svpattern { + match value { + 0 => svpattern::SV_POW2, + 1 => svpattern::SV_VL1, + 2 => svpattern::SV_VL2, + 3 => svpattern::SV_VL3, + 4 => svpattern::SV_VL4, + 5 => svpattern::SV_VL5, + 6 => svpattern::SV_VL6, + 7 => svpattern::SV_VL7, + 8 => svpattern::SV_VL8, + 9 => svpattern::SV_VL16, + 10 => svpattern::SV_VL32, + 11 => svpattern::SV_VL64, + 12 => svpattern::SV_VL128, + 13 => svpattern::SV_VL256, + 29 => svpattern::SV_MUL4, + 30 => svpattern::SV_MUL3, + 31 => svpattern::SV_ALL, + _ => unreachable!(), + } +} + +#[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] +const fn svprfop_from_i32(value: i32) -> svprfop { + match value { + 0 => svprfop::SV_PLDL1KEEP, + 1 => svprfop::SV_PLDL1STRM, + 2 => svprfop::SV_PLDL2KEEP, + 3 => svprfop::SV_PLDL2STRM, + 4 => svprfop::SV_PLDL3KEEP, + 5 => svprfop::SV_PLDL3STRM, + 8 => svprfop::SV_PSTL1KEEP, + 9 => svprfop::SV_PSTL1STRM, + 10 => svprfop::SV_PSTL2KEEP, + 11 => svprfop::SV_PSTL2STRM, + 12 => svprfop::SV_PSTL3KEEP, + 13 => svprfop::SV_PSTL3STRM, + _ => unreachable!(), + } +} + +macro_rules! debug_print_integral { + ($($name:ident => ($ty:ty, $svptrue_fn:ident, $svcnt_fn:ident, $svst_fn:ident)),*) => { + $( + #[inline] + #[target_feature(enable = "sve")] + #[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] + pub fn $name(v: $ty) -> String { + unsafe { + let __pred = $svptrue_fn(); + let __num_elems = $svcnt_fn() as usize; + let mut __buf = std::vec::Vec::with_capacity(__num_elems); + $svst_fn(__pred, __buf.as_mut_ptr(), v); + __buf.set_len(__num_elems); + format!( + "[{}]", + __buf.iter().map(|el| el.to_string()).collect::>().join(", ") + ) + } + } + )* + } +} + +debug_print_integral! { + debug_print_f32 => (svfloat32_t, svptrue_b32, svcntw, svst1_f32), + debug_print_f64 => (svfloat64_t, svptrue_b64, svcntd, svst1_f64), + debug_print_s8 => (svint8_t, svptrue_b8, svcntb, svst1_s8), + debug_print_s16 => (svint16_t, svptrue_b16, svcnth, svst1_s16), + debug_print_s32 => (svint32_t, svptrue_b32, svcntw, svst1_s32), + debug_print_s64 => (svint64_t, svptrue_b64, svcntd, svst1_s64), + debug_print_u8 => (svuint8_t, svptrue_b8, svcntb, svst1_u8), + debug_print_u16 => (svuint16_t, svptrue_b16, svcnth, svst1_u16), + debug_print_u32 => (svuint32_t, svptrue_b32, svcntw, svst1_u32), + debug_print_u64 => (svuint64_t, svptrue_b64, svcntd, svst1_u64) +} + +macro_rules! debug_print_bool { + ($($name:ident => ($ty:ty, $svst_fn:ident, $svdup_fn:ident)),*) => { + $( + #[inline] + #[target_feature(enable = "sve")] + #[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] + pub fn $name(v: $ty) -> String { + unsafe { + let __num_elems = svcntb() as usize; + let mut __buf = std::vec::Vec::with_capacity(__num_elems); + $svst_fn(v, __buf.as_mut_ptr(), $svdup_fn(1)); + __buf.set_len(__num_elems); + format!( + "[{}]", + __buf.iter() + .map(|el| *el == 1) + .map(|el| el.to_string()) + .collect::>() + .join(", ") + ) + } + } + )* + } +} + +debug_print_bool! { + debug_print_b8 => (svbool_t, svst1_u8, svdup_n_u8), + debug_print_b16 => (svbool_t, svst1_u16, svdup_n_u16), + debug_print_b32 => (svbool_t, svst1_u32, svdup_n_u32), + debug_print_b64 => (svbool_t, svst1_u64, svdup_n_u64) +} "#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index 7754e9ec2d0dd..7481bb4aeb30d 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,5 +1,9 @@ use super::intrinsic::ArmType; -use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; +use crate::common::PREDICATE_LOCAL; +use crate::common::intrinsic_helpers::{ + IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind, default_fixed_vector_comparison, +}; +use itertools::Itertools; impl TypeDefinition for ArmType { /// Gets a string containing the typename for this type in C format. @@ -70,32 +74,137 @@ impl TypeDefinition for ArmType { } } + fn rust_scalar_type_for_test_value_array(&self) -> String { + if self.kind() == TypeKind::Bool && self.num_lanes() == SimdLen::Scalable { + let mut ty = self.clone(); + ty.kind = TypeKind::Int(Sign::Signed); + ty.rust_scalar_type() + } else { + self.rust_scalar_type() + } + } + /// Determines the load function for this type. fn load_function(&self) -> String { - if let IntrinsicType { - kind: k, - bit_len: Some(bl), - vec_len, - .. - } = **self - { - let quad = if self.num_lanes() * bl > 64 { "q" } else { "" }; + if let Some(bl) = self.bit_len { + match self.num_lanes() { + SimdLen::Scalable => { + format!( + "svld{len}_{type}{bl}", + len = self.num_vectors(), + type = self.rust_intrinsic_name_prefix(), + ) + } + SimdLen::Fixed(num_lanes) => { + format!( + "vld{len}{quad}_{type}{bl}", + quad = if num_lanes * bl > 64 { "q" } else { "" }, + len = self.num_vectors(), + type = self.rust_intrinsic_name_prefix(), + ) + } + } + } else { + todo!("load_function IntrinsicType: {self:#?}") + } + } + + fn comparison_function(&self) -> String { + if let SimdLen::Fixed(num_lanes) = self.num_lanes() { + return default_fixed_vector_comparison(self, num_lanes); + } + + if self.kind() == TypeKind::Bool { + // There isn't a `svcmpeq` for `svbool_t` and there aren't `svboolxN_t` types, so just + // do an XOR and test it is empty. + return format!( + r#" +let __eq = sveor_b_z({PREDICATE_LOCAL}, __rust_return_value, __c_return_value); +assert!(!svptest_any({PREDICATE_LOCAL}, __eq), "{{}}", id); + "# + ); + } + + // Returns `of` when `num_vectors == 1` otherwise returns the appropriate `svget` invocation + // for `of`. + let get = |num_vectors: u32, idx: u32, from: &'static str| -> String { + if num_vectors == 1 { + return from.to_string(); + } format!( - "vld{len}{quad}_{type}{size}", - type = match k { - TypeKind::Int(Sign::Unsigned) => "u", - TypeKind::Int(Sign::Signed) => "s", - TypeKind::Float => "f", - TypeKind::Poly => "p", - x => todo!("get_load_function TypeKind: {x:#?}"), - }, - size = bl, - quad = quad, - len = vec_len.unwrap_or(1), + "svget{num_vectors}_{ty}{bl}::<{idx}>({from})", + ty = self.rust_intrinsic_name_prefix(), + bl = self.inner_size(), ) - } else { - todo!("load_function IntrinsicType: {self:#?}") + }; + + let n = self.num_vectors(); + (0..n) + .format_with("\n", |i, fmt| { + match self.kind() { + TypeKind::Float | TypeKind::BFloat => { + // Floats need special handling because `NaN != NaN` normally - this + // effectively does `(rust == c) || (isnan(rust) && isnan(c))` + fmt(&format_args!( + r#" +let __rust_eq_return_value = {rust_return_value}; +let __c_eq_return_value = {c_return_value}; +let __eq_sans_nan = svcmpeq_{ty}{bl}({PREDICATE_LOCAL}, __rust_eq_return_value, __c_eq_return_value); +let __rust_nan = svcmpuo_{ty}{bl}({PREDICATE_LOCAL}, __rust_eq_return_value, __rust_eq_return_value); +let __c_nan = svcmpuo_{ty}{bl}({PREDICATE_LOCAL}, __c_eq_return_value, __c_eq_return_value); +let __both_nan = svand_b_z({PREDICATE_LOCAL}, __rust_nan, __c_nan); +let __eq = svorr_b_z({PREDICATE_LOCAL}, __eq_sans_nan, __both_nan); +if !svptest_any(__pred, __eq) {{ + let __rust_pretty = debug_print_{ty}{bl}(__rust_eq_return_value); + let __c_pretty = debug_print_{ty}{bl}(__c_eq_return_value); + panic!("{{}}-{i_plus_one}/{n}\nRust: {{__rust_pretty}}\nC: {{__c_pretty}}", id); +}} +"#, + ty = self.rust_intrinsic_name_prefix(), + bl = self.inner_size(), + rust_return_value = get(n, i, "__rust_return_value"), + c_return_value = get(n, i, "__c_return_value"), + i_plus_one = i + 1, // so that the output is "1/2" and "2/2" + )) + } + _ => { + // Most types can just use `svcmpeq` + fmt(&format_args!( + r#" +let __rust_eq_return_value = {rust_return_value}; +let __c_eq_return_value = {c_return_value}; +let __eq = svcmpeq_{ty}{bl}({PREDICATE_LOCAL}, __rust_eq_return_value, __c_eq_return_value); +if !svptest_any(__pred, __eq) {{ + let __rust_pretty = debug_print_{ty}{bl}(__rust_eq_return_value); + let __c_pretty = debug_print_{ty}{bl}(__c_eq_return_value); + panic!("{{}}-{i_plus_one}/{n}\nRust: {{__rust_pretty}}\nC: {{__c_pretty}}", id); +}} +"#, + ty = self.rust_intrinsic_name_prefix(), + bl = self.inner_size(), + rust_return_value = get(n, i, "__rust_return_value"), + c_return_value = get(n, i, "__c_return_value"), + i_plus_one = i + 1, // so that the output is "1/2" and "2/2" + )) + } + } + }) + .to_string() + } +} + +impl ArmType { + /// Returns the Rust prefix for the name of an intrinsic with this type kind (i.e. `s` for + /// `i16`, or `u` for `u16`). For type kinds without any bit length at the end (e.g. `bool`), + /// returns the whole type name. + pub fn rust_intrinsic_name_prefix(&self) -> &str { + match self.kind() { + TypeKind::Char(Sign::Signed) => "s", + TypeKind::Int(Sign::Signed) => "s", + TypeKind::Poly => "p", + TypeKind::Bool => "s", + _ => self.kind.rust_prefix(), } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index 4d38bce327930..10d9224183377 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -4,9 +4,9 @@ use crate::common::SupportedArchitecture; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::test_values_array_name; -use super::PASSES; use super::constraint::Constraint; use super::intrinsic_helpers::TypeDefinition; +use super::{PASSES, PREDICATE_LOCAL}; /// An argument for the intrinsic. #[derive(Debug, PartialEq, Clone)] @@ -19,18 +19,27 @@ pub struct Argument { pub ty: A::Type, /// Any constraints that are on this argument pub constraint: Option, + /// Is the argument a predicate for a scalable intrinsic? + pub is_predicate: bool, } impl Argument where A: SupportedArchitecture, { - pub fn new(pos: usize, name: String, ty: A::Type, constraint: Option) -> Self { + pub fn new( + pos: usize, + name: String, + ty: A::Type, + constraint: Option, + is_predicate: bool, + ) -> Self { Argument { pos, name, ty, constraint, + is_predicate, } } @@ -38,8 +47,14 @@ where self.ty.c_type() } + /// Generates local variable name for the value passed to this argument pub fn generate_name(&self) -> String { - format!("{}_val", self.name) + // The same predicate is used for scalable intrinsic invocations + if self.is_predicate { + format!("{PREDICATE_LOCAL}") + } else { + format!("{}_val", self.name) + } } pub fn is_simd(&self) -> bool { @@ -176,15 +191,11 @@ where pub fn load_values_rust(&self) -> String { self.iter() .filter(|&arg| !arg.has_constraint()) + .filter(|&arg| !arg.is_predicate) .enumerate() .map(|(idx, arg)| { if arg.is_simd() { - format!( - "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);", - name = arg.generate_name(), - vals_name = test_values_array_name(&arg.ty), - load = arg.ty.load_function(), - ) + A::load_call(arg, idx) } else { format!( "let {name} = {vals_name}[(i+{idx}) % {PASSES}];", diff --git a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs index ab52d866ab20a..c7d37da2adcbb 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::ops::Range; +use serde::Deserialize; + /// Describes the values to test for a const generic parameter #[derive(Debug, PartialEq, Clone, Deserialize)] pub enum Constraint { @@ -23,21 +24,53 @@ pub enum Constraint { SvImmRotationAdd, } -impl Constraint { - /// Returns an iterator over the values of this constraint - pub fn iter(&self) -> Box + '_> { +/// Workaround to enable the `Constraint::into_iter` to return an iterator that implements `Clone`, +/// so that it can be used with `Itertools::multi_cartesian_product`. +/// +/// With the different iterator types, returning `Box + '_>` would the +/// idiomatic approach, but this can't be made to implement `Clone`. Given the limited number +/// of iterator types used and their relative lack of complexity, wrapping them all in an enum isn't +/// too bad. +#[derive(Clone)] +pub enum ConstraintIterator<'a> { + Once(std::iter::Once), + Range(std::ops::Range), + Copied(std::iter::Copied>), + Chain(std::iter::Chain, std::ops::RangeInclusive>), + StepBy(std::iter::StepBy>), +} + +impl<'a> Iterator for ConstraintIterator<'a> { + type Item = i64; + + fn next(&mut self) -> Option { + match self { + ConstraintIterator::Once(once) => once.next(), + ConstraintIterator::Range(range) => range.next(), + ConstraintIterator::Copied(copied) => copied.next(), + ConstraintIterator::Chain(chain) => chain.next(), + ConstraintIterator::StepBy(step_by) => step_by.next(), + } + } +} + +impl<'a> IntoIterator for &'a Constraint { + type Item = i64; + type IntoIter = ConstraintIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { match self { - Constraint::Equal(i) => Box::new(std::iter::once(*i)), - Constraint::Range(range) => Box::new(range.clone()), - Constraint::Set(items) => Box::new(items.iter().copied().chain(Range::default())), + Constraint::Equal(i) => ConstraintIterator::Once(std::iter::once(*i)), + Constraint::Range(range) => ConstraintIterator::Range(range.clone()), + Constraint::Set(items) => ConstraintIterator::Copied(items.iter().copied()), // These values are discriminants of the `svpattern` enum - Constraint::SvPattern => Box::new((0..=13).chain(29..=31)), + Constraint::SvPattern => ConstraintIterator::Chain((0..=13).chain(29..=31)), // These values are discriminants of the `svprfop` enum - Constraint::SvPrefetchOp => Box::new((0..=5).chain(8..=14)), + Constraint::SvPrefetchOp => ConstraintIterator::Chain((0..=5).chain(8..=14)), // Valid rotations for intrinsics operating on complex pairs: 0, 90, 180, 270 - Constraint::SvImmRotation => Box::new((0..=270).step_by(90)), + Constraint::SvImmRotation => ConstraintIterator::StepBy((0..=270).step_by(90)), // Valid rotations for `svcadd` and `svqcadd`: 0, 270 - Constraint::SvImmRotationAdd => Box::new((90..=270).step_by(180)), + Constraint::SvImmRotationAdd => ConstraintIterator::StepBy((90..=270).step_by(180)), } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 21cc5cfa2e597..bbff7a91b6c41 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -18,30 +18,36 @@ pub fn write_wrapper_c( w: &mut impl std::io::Write, intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{}", A::NOTICE)?; + write!( + w, + r#" +{notice} +#include +#include +{prelude} - writeln!(w, "#include ")?; - writeln!(w, "#include ")?; - writeln!(w, "{}", A::C_PRELUDE)?; - - for intrinsic in intrinsics { - intrinsic.iter_specializations(|imm_values| { - writeln!( - w, - " +{intrinsics} +"#, + notice = A::NOTICE, + prelude = A::C_PRELUDE, + intrinsics = intrinsics.iter().format_with("", |intrinsic, fmt| { + fmt(&intrinsic + .specializations() + .format_with("\n", |imm_values, fmt| { + fmt(&format_args!( + " void {name}_wrapper{imm_arglist}({return_ty}* __dst{arglist}) {{ *__dst = {name}({params}); }}", - return_ty = intrinsic.results.c_type(), - name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), - arglist = intrinsic.arguments.as_non_imm_arglist_c(), - params = intrinsic.arguments.as_call_params_c(&imm_values) - ) - })?; - } - - Ok(()) + return_ty = intrinsic.results.c_type(), + name = intrinsic.name, + imm_arglist = imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + arglist = intrinsic.arguments.as_non_imm_arglist_c(), + params = intrinsic.arguments.as_call_params_c(&imm_values) + )) + })) + }), + ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index baa11511ec219..44128d43b99a0 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -5,8 +5,9 @@ use itertools::Itertools; use super::intrinsic_helpers::TypeDefinition; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; +use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; -use crate::common::{PASSES, SupportedArchitecture}; +use crate::common::{PASSES, PREDICATE_LOCAL, SupportedArchitecture}; /// Rust definitions that are included verbatim in the generated source. In particular, defines /// a wrapper around float types that defines `NaN`s to be equal reflexively to enable @@ -126,7 +127,10 @@ pub fn write_lib_rs( for intrinsic in intrinsics { for arg in &intrinsic.arguments.args { - if !arg.has_constraint() { + // Skip arguments with constraints as these correspond to generic instantiatons, and + // predicates for scalable intrinsics as the same predicate is used for all intrinsics + // under test. + if !arg.has_constraint() && !arg.is_predicate { let name = test_values_array_name(&arg.ty); if seen.insert(name) { @@ -168,38 +172,14 @@ fn generate_rust_test_loop( coerce += ") -> _"; c_coerce += ")"; - if intrinsic - .arguments - .iter() - .filter(|arg| arg.has_constraint()) - .count() - == 0 - { - writeln!( - w, - " let specializations = [(\"\", {intrinsic_name}, {intrinsic_name}_wrapper)];" - )?; - } else { - writeln!(w, " let specializations = [")?; - - intrinsic.iter_specializations(|imm_values| { - writeln!( - w, - " (\"{const_args}\", {intrinsic_name}::<{const_args}> as unsafe {coerce}, {intrinsic_name}_wrapper_{c_const_args} as unsafe extern \"C\" {c_coerce}),", - const_args = imm_values.iter().join(","), - c_const_args = imm_values.iter().join("_"), - ) - })?; - - writeln!(w, " ];")?; - } - write!( w, r#" +let specializations = [{specializations}]; for (id, rust, c) in specializations {{ for i in 0..{PASSES} {{ unsafe {{ + {predicate} {loaded_args} let __rust_return_value = rust({rust_args}); @@ -212,9 +192,50 @@ for (id, rust, c) in specializations {{ }} }} "#, + specializations = intrinsic + .specializations() + .format_with(",", |imm_values, fmt| { + if imm_values.is_empty() { + fmt(&format_args!( + "(\"\", {intrinsic_name}, {intrinsic_name}_wrapper)" + )) + } else { + let constraint_args = intrinsic.arguments.iter().filter(|a| a.has_constraint()); + fmt(&format_args!( + r#" + ( + "{const_args}", + {intrinsic_name}::<{const_args}> as unsafe {coerce}, + {intrinsic_name}_wrapper_{c_const_args} as unsafe extern "C" {c_coerce} + ) + "#, + const_args = imm_values + .iter() + .zip(constraint_args) + .map(|(imm_val, arg)| { + match arg.ty.kind() { + TypeKind::SvPattern | TypeKind::SvPrefetchOp => { + format!("{{ {}_from_i32({imm_val}) }}", arg.ty.kind()) + } + _ => imm_val.to_string(), + } + }) + .join(","), + c_const_args = imm_values.iter().join("_"), + )) + } + }), loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), + predicate = if intrinsic.has_scalable_argument_or_result() { + format!( + "let {PREDICATE_LOCAL} = {pred};", + pred = A::predicate_function(intrinsic.results.inner_size()), + ) + } else { + "".to_string() + }, comparison = intrinsic.results.comparison_function(), ) } @@ -256,25 +277,25 @@ pub fn write_bindings_rust( #[allow(improper_ctypes)] #[link(name = "wrapper_{i}")] unsafe extern "C" {{ + {definitions} +}} "#, - )?; - - for intrinsic in intrinsics { - intrinsic.iter_specializations(|imm_values| { - writeln!( - w, - "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", - return_ty = intrinsic.results.rust_type(), - name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), - arglist = intrinsic.arguments.as_non_imm_arglist_rust(), - ) - })?; - } - - writeln!(w, "}}") + definitions = intrinsics.iter().format_with("", |intrinsic, fmt| { + fmt(&intrinsic + .specializations() + .format_with("\n", |imm_values, fmt| { + fmt(&format_args!( + "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", + return_ty = intrinsic.results.rust_type(), + name = intrinsic.name, + imm_arglist = imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + arglist = intrinsic.arguments.as_non_imm_arglist_rust(), + )) + })) + }) + ) } /// Writes a `build.rs` into `w` for each test crate that compiles the corresponding C source code diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 0c5bf43069d00..d5d903d941e4b 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,5 +1,6 @@ use super::argument::ArgumentList; -use crate::common::{SupportedArchitecture, constraint::Constraint}; +use crate::common::{SupportedArchitecture, intrinsic_helpers::SimdLen}; +use itertools::Itertools; /// An intrinsic #[derive(Debug, PartialEq, Clone)] @@ -20,27 +21,6 @@ pub struct Intrinsic { pub extension: String, } -/// Invokes `f` for each combination of the values in the constraint ranges. -/// -/// For example, given `constraints=[Equal(0), Range(1..2), Set([3, 4])]` and `imm_values=[]`, this -/// produces the four calls to `f`: `f([0, 1, 3])`, `f([0, 1, 4])`, `f([0, 2, 3])`, `f([0, 2, 4])`. -fn recurse_specializations<'a, E>( - constraints: &mut (impl Iterator + Clone), - imm_values: &mut Vec, - f: &mut impl FnMut(&[i64]) -> Result<(), E>, -) -> Result<(), E> { - if let Some(current) = constraints.next() { - for i in current.iter() { - imm_values.push(i); - recurse_specializations(&mut constraints.clone(), imm_values, f)?; - imm_values.pop(); - } - Ok(()) - } else { - f(&imm_values) - } -} - impl Intrinsic { /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds @@ -49,17 +29,20 @@ impl Intrinsic { /// For an intrinsic with three arguments with constraints `Equal(0)`, `Range(1..2)`, /// `Set([3, 4])` respectively, this would produce four calls to `f`: `f(0, 1, 3)`, /// `f(0, 1, 4)`, `f(0, 2, 3)`, `f(0, 2, 4)`. - pub fn iter_specializations( - &self, - mut f: impl FnMut(&[i64]) -> Result<(), E>, - ) -> Result<(), E> { - recurse_specializations( - &mut self + pub fn specializations(&self) -> impl Iterator> { + self.arguments + .iter() + .filter_map(|arg| arg.constraint.as_ref()) + .map(|constraint| constraint.into_iter()) + .multi_cartesian_product() + } + + /// Returns `true` if this intrinsic has any argument or result types that are scalable vectors + pub fn has_scalable_argument_or_result(&self) -> bool { + self.results.num_lanes() == SimdLen::Scalable + || self .arguments .iter() - .filter_map(|arg| arg.constraint.as_ref()), - &mut Vec::new(), - &mut f, - ) + .any(|a| a.ty.num_lanes() == SimdLen::Scalable) } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index b9f30af7dfd51..bc47a90d3fe16 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -116,6 +116,15 @@ pub enum SimdLen { Fixed(u32), } +impl SimdLen { + pub fn expect_fixed(&self) -> u32 { + match self { + SimdLen::Fixed(lanes) => *lanes, + SimdLen::Scalable => panic!("`expect_fixed` with scalable length"), + } + } +} + impl std::fmt::Display for SimdLen { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -173,14 +182,8 @@ impl IntrinsicType { } /// Returns the number of lanes of the type - pub fn num_lanes(&self) -> u32 { - self.simd_len - .as_ref() - .map(|len| match len { - SimdLen::Scalable => unimplemented!(), - SimdLen::Fixed(len) => *len, - }) - .unwrap_or(1) + pub fn num_lanes(&self) -> SimdLen { + self.simd_len.unwrap_or(SimdLen::Fixed(1)) } /// Returns the number of vectors of the type @@ -208,7 +211,7 @@ pub trait TypeDefinition: Clone + DerefMut { match self.simd_len { Some(SimdLen::Scalable) => unimplemented!("architecture-specific"), Some(SimdLen::Fixed(_)) | None => { - default_fixed_vector_comparison(self, self.num_lanes()) + default_fixed_vector_comparison(self, self.num_lanes().expect_fixed()) } } } @@ -227,6 +230,12 @@ pub trait TypeDefinition: Clone + DerefMut { ty.vec_len = None; ty.rust_type() } + + /// Gets a string containing the name of the scalar type corresponding to this type that should + /// be used as the element type for the test value array. + fn rust_scalar_type_for_test_value_array(&self) -> String { + self.rust_scalar_type() + } } /// Returns the default comparison between results of an intrinsic - casting the vectors to arrays diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 73daabbd6661a..46b6bc4102bed 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -5,12 +5,14 @@ use rayon::prelude::*; use cli::ProcessedCli; use crate::common::{ + argument::Argument, gen_c::write_wrapper_c, gen_rust::{ run_rustfmt, write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs, }, intrinsic::Intrinsic, intrinsic_helpers::TypeDefinition, + values::test_values_array_name, }; pub mod argument; @@ -18,10 +20,16 @@ pub mod cli; pub mod constraint; pub mod intrinsic; pub mod intrinsic_helpers; +pub mod values; mod gen_c; mod gen_rust; -mod values; + +/// Many scalable intrinsics take a predicate argument and for the purposes of intrinsic testing, +/// a predicate that enables all lanes is used for all of these intrinsic calls (i.e. loading inputs, +/// result comparison, and the intrinsic under test). This constant defines the name of the local +/// variable that contains that predicate. +pub const PREDICATE_LOCAL: &'static str = "__pred"; // The number of times each intrinsic will be called - influences the generation of the // test arrays to minimise repeated testing of the same test values. @@ -100,6 +108,19 @@ pub trait SupportedArchitecture: Sized { .collect::>() .unwrap(); } + + /// Return a call to a intrinsic to generate a predicate, if reqd. + fn predicate_function(_: u32) -> String; + + /// Return a call loading `arg`. Can assume that `arg.is_simd()` holds. + fn load_call(arg: &Argument, idx: usize) -> String { + format!( + "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", + name = arg.generate_name(), + vals_name = test_values_array_name(&arg.ty), + load = arg.ty.load_function(), + ) + } } pub fn manual_chunk(intrinsic_count: usize) -> (usize, usize) { diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index 8c549346ce6ed..d1f333fd19363 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -26,7 +26,7 @@ pub fn test_values_array_static( w, "static {name}: [{ty}; {load_size}] = {values};", name = test_values_array_name(ty), - ty = ty.rust_scalar_type(), + ty = ty.rust_scalar_type_for_test_value_array(), load_size = test_values_array_length(&ty), values = test_values_array(&ty) ) @@ -50,8 +50,16 @@ pub fn test_values_array_name(ty: &T) -> String { /// which is then printed as a hex value in the generated code (and if identified as a negative /// value, with the appropriate minus and corrected hex pattern). Calls to `fN::from_bits` are /// generated for floats. +/// +/// An exception to the above is when `ty` is a boolean, where this function returns +/// `[true, false]` - as there are only ever two values for a boolean. This only works because the +/// generated accesses to the test value array is always modulo the length of the test value array. pub fn test_values_array(ty: &IntrinsicType) -> String { let (bit_len, kind) = match ty { + IntrinsicType { + kind: TypeKind::Bool, + .. + } => (1, TypeKind::Bool), IntrinsicType { kind: TypeKind::Float, bit_len: Some(bit_len), @@ -75,6 +83,9 @@ pub fn test_values_array(ty: &IntrinsicType) -> String { let src = bit_pattern_for_test_values_array(bit_len, i); assert!(src == 0 || src.ilog2() < bit_len); match kind { + TypeKind::Bool if ty.num_lanes() != SimdLen::Scalable => { + fmt(&format_args!("{}", if src == 1 { true } else { false })) + } TypeKind::Float => fmt(&format_args!("f{bit_len}::from_bits({src:#x})")), TypeKind::Vector | TypeKind::Int(Sign::Signed) if (src >> (bit_len - 1)) != 0 => { // `src` is a two's complement representation of a negative value. @@ -134,7 +145,8 @@ pub fn test_values_array_length(ty: &IntrinsicType) -> u32 { pub fn bit_pattern_for_test_values_array(bits: u32, index: u32) -> u64 { let index = index as usize; match bits { - bits @ (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8) => BIT_PATTERNS_8[index % (1 << bits)].into(), + 1 => BIT_PATTERNS_1[index % BIT_PATTERNS_1.len()].into(), + bits @ (2 | 3 | 4 | 5 | 6 | 7 | 8) => BIT_PATTERNS_8[index % (1 << bits)].into(), 16 => BIT_PATTERNS_16[index % BIT_PATTERNS_16.len()].into(), 32 => BIT_PATTERNS_32[index % BIT_PATTERNS_32.len()].into(), 64 => BIT_PATTERNS_64[index % BIT_PATTERNS_64.len()], @@ -142,6 +154,9 @@ pub fn bit_pattern_for_test_values_array(bits: u32, index: u32) -> u64 { } } +// Contains every possible 1-bit value in order +pub const BIT_PATTERNS_1: &[u8] = &[0x0, 0x1]; + // Contains every possible 8-bit value in order pub const BIT_PATTERNS_8: &[u8] = &[ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index ce4955032916d..36f4fee43741b 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -99,6 +99,10 @@ impl SupportedArchitecture for X86 { Self { intrinsics } } + + fn predicate_function(_: u32) -> String { + unimplemented!("no scalable vectors on x86") + } } const RUST_PRELUDE: &str = r#" diff --git a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs index 52f6da786e3d7..2296dffb64a5b 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs @@ -99,8 +99,13 @@ fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box::new(i, param.var_name.clone(), ty.unwrap(), constraint); - Some(arg) + Some(Argument::::new( + i, + param.var_name.clone(), + ty.unwrap(), + constraint, + false, + )) } }); diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml index 138d5ba31175b..0b2d17d3b195f 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml @@ -1157,7 +1157,7 @@ intrinsics: doc: Interleave even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [trn1] compose: - LLVMLink: { name: "trn1.{sve_type}" } @@ -1167,10 +1167,13 @@ intrinsics: doc: Interleave even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [trn1] compose: - - LLVMLink: { name: "trn1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.trn1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svtrn1q[_{type}] attr: [*sve-unstable] @@ -1188,7 +1191,7 @@ intrinsics: doc: Interleave odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [trn2] compose: - LLVMLink: { name: "trn2.{sve_type}" } @@ -1198,10 +1201,13 @@ intrinsics: doc: Interleave odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [trn2] compose: - - LLVMLink: { name: "trn2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.trn2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svtrn2q[_{type}] attr: [*sve-unstable] diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/Cargo.toml b/library/stdarch/crates/stdarch-gen-hexagon-scalar/Cargo.toml deleted file mode 100644 index 04bee944f4a91..0000000000000 --- a/library/stdarch/crates/stdarch-gen-hexagon-scalar/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "stdarch-gen-hexagon-scalar" -version = "0.1.0" -authors = ["The Rust Project Developers"] -license = "MIT OR Apache-2.0" -edition = "2021" - -[dependencies] -regex = "1.10" diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/hexagon_protos.h b/library/stdarch/crates/stdarch-gen-hexagon/hexagon_protos.h similarity index 100% rename from library/stdarch/crates/stdarch-gen-hexagon-scalar/hexagon_protos.h rename to library/stdarch/crates/stdarch-gen-hexagon/hexagon_protos.h diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs new file mode 100644 index 0000000000000..4d4b03c3ac204 --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs @@ -0,0 +1,1648 @@ +//! Hexagon HVX Code Generator +//! +//! This generator creates v64.rs and v128.rs from scratch using the LLVM HVX +//! header file as the sole source of truth. It parses the C intrinsic prototypes +//! and generates Rust wrapper functions with appropriate attributes. +//! +//! The two generated files provide: +//! - v64.rs: 64-byte vector mode intrinsics (512-bit vectors) +//! - v128.rs: 128-byte vector mode intrinsics (1024-bit vectors) +//! +//! Both modules are available unconditionally, but require the appropriate +//! target features to actually use the intrinsics. +//! +//! Usage: +//! cd crates/stdarch-gen-hexagon +//! cargo run +//! # Output is written to ../core_arch/src/hexagon/v64.rs and v128.rs + +use regex::Regex; +use std::collections::{HashMap, HashSet}; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use stdarch_gen_common::GENERATED_MARKER; + +/// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. +/// These intrinsics have equivalent semantics and can be lowered to the generic form. +fn get_simd_intrinsic_mappings() -> HashMap<&'static str, &'static str> { + let mut map = HashMap::new(); + // Bitwise operations (element-size independent) + map.insert("vxor", "simd_xor"); + map.insert("vand", "simd_and"); + map.insert("vor", "simd_or"); + // Word (32-bit) arithmetic operations + map.insert("vaddw", "simd_add"); + map.insert("vsubw", "simd_sub"); + map +} + +/// The tracking issue number for the stdarch_hexagon feature +const TRACKING_ISSUE: &str = "151523"; + +/// HVX vector length mode +#[derive(Debug, Clone, Copy, PartialEq)] +enum VectorMode { + /// 64-byte vectors (512 bits) + V64, + /// 128-byte vectors (1024 bits) + V128, +} + +impl VectorMode { + fn bytes(&self) -> u32 { + match self { + VectorMode::V64 => 64, + VectorMode::V128 => 128, + } + } + + fn bits(&self) -> u32 { + self.bytes() * 8 + } + + fn lanes(&self) -> u32 { + self.bytes() / 4 // 32-bit lanes + } + + fn target_feature(&self) -> &'static str { + match self { + VectorMode::V64 => "hvx-length64b", + VectorMode::V128 => "hvx-length128b", + } + } +} + +/// LLVM version the header file is from (for reference) +/// Source: https://github.com/llvm/llvm-project/blob/llvmorg-22.1.0-rc1/clang/lib/Headers/hvx_hexagon_protos.h +const LLVM_VERSION: &str = "22.1.0-rc1"; + +/// Maximum HVX architecture version supported by rustc +/// Check with: rustc --target=hexagon-unknown-linux-musl --print target-features +const MAX_SUPPORTED_ARCH: u32 = 79; + +/// Local header file path (checked into the repository) +const HEADER_FILE: &str = "hvx_hexagon_protos.h"; + +/// Intrinsic information parsed from the LLVM header +#[derive(Debug, Clone)] +struct IntrinsicInfo { + /// The Q6_* intrinsic name (e.g., "Q6_V_vadd_VV") + q6_name: String, + /// The LLVM builtin name without prefix (e.g., "V6_vaddb") + builtin_name: String, + /// The short instruction name for assert_instr (e.g., "vaddb") + instr_name: String, + /// The assembly syntax from the comment + asm_syntax: String, + /// Instruction type + instr_type: String, + /// Execution slots + exec_slots: String, + /// Minimum HVX architecture version required + min_arch: u32, + /// Return type + return_type: RustType, + /// Parameters (name, type) + params: Vec<(String, RustType)>, + /// Whether this is a compound intrinsic (multiple builtins) + is_compound: bool, + /// For compound intrinsics: the parsed expression tree + compound_expr: Option, +} + +/// Expression tree for compound intrinsics +#[derive(Debug, Clone)] +enum CompoundExpr { + /// A call to a builtin: (builtin_name without V6_ prefix, arguments) + BuiltinCall(String, Vec), + /// A parameter reference by name + Param(String), + /// An integer literal (like -1) + IntLiteral(i32), +} + +/// Rust type mappings +#[derive(Debug, Clone, PartialEq)] +enum RustType { + HvxVector, + HvxVectorPair, + HvxVectorPred, + I32, + MutPtrHvxVector, + Unit, +} + +impl RustType { + fn from_c_type(c_type: &str) -> Option { + match c_type.trim() { + "HVX_Vector" => Some(RustType::HvxVector), + "HVX_VectorPair" => Some(RustType::HvxVectorPair), + "HVX_VectorPred" => Some(RustType::HvxVectorPred), + "Word32" => Some(RustType::I32), + "HVX_Vector*" => Some(RustType::MutPtrHvxVector), + "void" => Some(RustType::Unit), + _ => None, + } + } + + fn to_rust_str(&self) -> &'static str { + match self { + RustType::HvxVector => "HvxVector", + RustType::HvxVectorPair => "HvxVectorPair", + RustType::HvxVectorPred => "HvxVectorPred", + RustType::I32 => "i32", + RustType::MutPtrHvxVector => "*mut HvxVector", + RustType::Unit => "()", + } + } + + fn to_extern_str(&self) -> &'static str { + match self { + RustType::HvxVector => "HvxVector", + RustType::HvxVectorPair => "HvxVectorPair", + RustType::HvxVectorPred => "HvxVectorPred", + RustType::I32 => "i32", + RustType::MutPtrHvxVector => "*mut HvxVector", + RustType::Unit => "()", + } + } +} + +/// Parse a compound macro expression into an expression tree +fn parse_compound_expr(expr: &str) -> Option { + let expr = expr.trim(); + + // Try to match an integer literal (like -1) + if let Ok(n) = expr.parse::() { + return Some(CompoundExpr::IntLiteral(n)); + } + + // Try to match a simple parameter name (Vu, Vv, Rt, Qs, Qt, Qx, Vx, etc.) + // These are typically short identifiers in the macro + if expr.len() <= 3 + && expr.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') + && !expr.contains("__") + { + return Some(CompoundExpr::Param(expr.to_lowercase())); + } + + // Check if it's wrapped in extra parens first + if expr.starts_with('(') && expr.ends_with(')') { + // Check if these parens wrap the entire expression + let inner = &expr[1..expr.len() - 1]; + // Count depth: if after removing outer parens the expression is balanced, + // the outer parens were enclosing everything + if is_balanced_parens(inner) { + // But we also need to verify these aren't part of a function call + // If the inner expression is balanced and the whole thing starts with ( + // and ends with ), it's a paren wrapper + let result = parse_compound_expr(inner); + if result.is_some() { + return result; + } + } + } + + // Try to match __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) + // The args portion may contain nested calls, so we need to find the matching paren + if expr.starts_with("__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_") { + // Find the end of the builtin name (after V6_) + let prefix = "__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_"; + let after_prefix = &expr[prefix.len()..]; + if let Some(paren_pos) = after_prefix.find(')') { + let builtin_name = &after_prefix[..paren_pos]; + let rest = &after_prefix[paren_pos + 1..]; // Skip the closing ) of the WRAP + // rest should now be "(args)" + if rest.starts_with('(') && rest.ends_with(')') { + let args_str = &rest[1..rest.len() - 1]; + let args = parse_compound_args(args_str)?; + return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); + } + } + } + + // Try to match __builtin_HEXAGON_V6_xxx(args) without wrap + if expr.starts_with("__builtin_HEXAGON_V6_") { + let prefix = "__builtin_HEXAGON_V6_"; + let after_prefix = &expr[prefix.len()..]; + if let Some(paren_pos) = after_prefix.find('(') { + let builtin_name = &after_prefix[..paren_pos]; + let rest = &after_prefix[paren_pos..]; + if rest.starts_with('(') && rest.ends_with(')') { + let args_str = &rest[1..rest.len() - 1]; + let args = parse_compound_args(args_str)?; + return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); + } + } + } + + None +} + +/// Check if parentheses are balanced in a string +fn is_balanced_parens(s: &str) -> bool { + let mut depth = 0; + for c in s.chars() { + match c { + '(' => depth += 1, + ')' => { + depth -= 1; + if depth < 0 { + return false; + } + } + _ => {} + } + } + depth == 0 +} + +/// Parse comma-separated arguments, respecting nested parentheses +fn parse_compound_args(args_str: &str) -> Option> { + let mut args = Vec::new(); + let mut current = String::new(); + let mut depth = 0; + + for c in args_str.chars() { + match c { + '(' => { + depth += 1; + current.push(c); + } + ')' => { + depth -= 1; + current.push(c); + } + ',' if depth == 0 => { + let arg = current.trim().to_string(); + if !arg.is_empty() { + args.push(parse_compound_expr(&arg)?); + } + current.clear(); + } + _ => current.push(c), + } + } + + // Don't forget the last argument + let arg = current.trim().to_string(); + if !arg.is_empty() { + args.push(parse_compound_expr(&arg)?); + } + + Some(args) +} + +/// Extract all builtin names used in a compound expression +fn collect_builtins_from_expr(expr: &CompoundExpr, builtins: &mut HashSet) { + match expr { + CompoundExpr::BuiltinCall(name, args) => { + builtins.insert(name.clone()); + for arg in args { + collect_builtins_from_expr(arg, builtins); + } + } + CompoundExpr::Param(_) | CompoundExpr::IntLiteral(_) => {} + } +} + +/// Read the local HVX header file +fn read_header(crate_dir: &Path) -> Result { + let header_path = crate_dir.join(HEADER_FILE); + println!("Reading HVX header from: {}", header_path.display()); + println!(" (LLVM version: {})", LLVM_VERSION); + + std::fs::read_to_string(&header_path).map_err(|e| { + format!( + "Failed to read header file {}: {}", + header_path.display(), + e + ) + }) +} + +/// Parse a C function prototype to extract return type and parameters +fn parse_prototype(prototype: &str) -> Option<(RustType, Vec<(String, RustType)>)> { + // Pattern: ReturnType FunctionName(ParamType1 Param1, ParamType2 Param2, ...) + let proto_re = Regex::new(r"(\w+(?:\*)?)\s+Q6_\w+\(([^)]*)\)").unwrap(); + + if let Some(caps) = proto_re.captures(prototype) { + let return_type_str = caps[1].trim(); + let params_str = &caps[2]; + + let return_type = RustType::from_c_type(return_type_str)?; + + let mut params = Vec::new(); + if !params_str.trim().is_empty() { + // Pattern: Type Name or Type* Name + let param_re = Regex::new(r"(\w+\*?)\s+(\w+)").unwrap(); + for param in params_str.split(',') { + let param = param.trim(); + if let Some(pcaps) = param_re.captures(param) { + let ptype_str = pcaps[1].trim(); + let pname = pcaps[2].to_lowercase(); + if let Some(ptype) = RustType::from_c_type(ptype_str) { + params.push((pname, ptype)); + } else { + return None; // Unknown type + } + } + } + } + + Some((return_type, params)) + } else { + None + } +} + +/// Parse the LLVM header file to extract intrinsic information +fn parse_header(content: &str) -> Vec { + let mut intrinsics = Vec::new(); + + let arch_re = Regex::new(r"#if __HVX_ARCH__ >= (\d+)").unwrap(); + + // Regex to extract the simple builtin name from a macro body + // Match: __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) + let simple_builtin_re = + Regex::new(r"__BUILTIN_VECTOR_WRAP\(__builtin_HEXAGON_(\w+)\)\([^)]*\)\s*$").unwrap(); + + // Also handle builtins without VECTOR_WRAP + let simple_builtin_re2 = Regex::new(r"__builtin_HEXAGON_(\w+)\([^)]*\)\s*$").unwrap(); + + // Regex to extract Q6 name from #define + let q6_name_re = Regex::new(r"#define\s+(Q6_\w+)").unwrap(); + + // Regex to extract macro expression body + let macro_expr_re = Regex::new(r"#define\s+Q6_\w+\([^)]*\)\s+(.+)").unwrap(); + + let lines: Vec<&str> = content.lines().collect(); + let mut current_arch: u32 = 60; + let mut i = 0; + + while i < lines.len() { + // Track architecture version + if let Some(caps) = arch_re.captures(lines[i]) { + if let Ok(arch) = caps[1].parse() { + current_arch = arch; + } + } + + // Look for Assembly Syntax comment block + if lines[i].contains("Assembly Syntax:") { + let mut asm_syntax = String::new(); + let mut prototype = String::new(); + let mut instr_type = String::new(); + let mut exec_slots = String::new(); + + // Parse the comment block + let mut j = i; + while j < lines.len() && !lines[j].starts_with("#define") { + let line = lines[j]; + if line.contains("Assembly Syntax:") { + if let Some(pos) = line.find("Assembly Syntax:") { + asm_syntax = line[pos + 16..].trim().to_string(); + } + } else if line.contains("C Intrinsic Prototype:") { + if let Some(pos) = line.find("C Intrinsic Prototype:") { + prototype = line[pos + 22..].trim().to_string(); + } + } else if line.contains("Instruction Type:") { + if let Some(pos) = line.find("Instruction Type:") { + instr_type = line[pos + 17..].trim().to_string(); + } + } else if line.contains("Execution Slots:") { + if let Some(pos) = line.find("Execution Slots:") { + exec_slots = line[pos + 16..].trim().to_string(); + } + } + j += 1; + } + + // Now find the #define line + while j < lines.len() && !lines[j].starts_with("#define") { + j += 1; + } + + if j < lines.len() { + let define_line = lines[j]; + + // Extract Q6 name and check if it's simple or compound + if let Some(caps) = q6_name_re.captures(define_line) { + let q6_name = caps[1].to_string(); + + // Get the full macro body (handle line continuations) + let mut macro_body = define_line.to_string(); + let mut k = j; + while macro_body.trim_end().ends_with('\\') && k + 1 < lines.len() { + k += 1; + macro_body.push_str(lines[k]); + } + + // Try to extract simple builtin name + let builtin_name = simple_builtin_re + .captures(¯o_body) + .or_else(|| simple_builtin_re2.captures(¯o_body)) + .map(|bcaps| bcaps[1].to_string()); + + // Check if it's a compound intrinsic (multiple __builtin calls) + let builtin_count = macro_body.matches("__builtin_HEXAGON_").count(); + let is_compound = builtin_count > 1; + + // Parse prototype + if let Some((return_type, params)) = parse_prototype(&prototype) { + if is_compound { + // For compound intrinsics, parse the expression + // Extract the macro body after the parameter list + if let Some(expr_caps) = macro_expr_re.captures(¯o_body) { + let expr_str = expr_caps[1].trim().replace(['\n', '\\'], " "); + let expr_str = expr_str.trim(); + + if let Some(compound_expr) = parse_compound_expr(expr_str) { + // For compound intrinsics, we use the outermost builtin + // as the "primary" for the instruction name + let (primary_builtin, instr_name) = match &compound_expr { + CompoundExpr::BuiltinCall(name, _) => { + (name.clone(), name.clone()) + } + _ => continue, + }; + + intrinsics.push(IntrinsicInfo { + q6_name, + builtin_name: format!("V6_{}", primary_builtin), + instr_name, + asm_syntax, + instr_type, + exec_slots, + min_arch: current_arch, + return_type, + params, + is_compound: true, + compound_expr: Some(compound_expr), + }); + } + } + } else if let Some(builtin) = builtin_name { + // Extract short instruction name + let instr_name = builtin + .strip_prefix("V6_") + .map(|s| s.to_string()) + .unwrap_or_else(|| builtin.clone()); + + intrinsics.push(IntrinsicInfo { + q6_name, + builtin_name: builtin, + instr_name, + asm_syntax, + instr_type, + exec_slots, + min_arch: current_arch, + return_type, + params, + is_compound: false, + compound_expr: None, + }); + } + } + } + } + i = j; + } + i += 1; + } + + intrinsics +} + +/// Generate the module documentation +fn generate_module_doc(mode: VectorMode) -> String { + format!( + r#"//! Hexagon HVX {bytes}-byte vector mode intrinsics +//! +//! This module provides intrinsics for the Hexagon Vector Extensions (HVX) +//! in {bytes}-byte vector mode ({bits}-bit vectors). +//! +//! HVX is a wide vector extension designed for high-performance signal processing. +//! [Hexagon HVX Programmer's Reference Manual](https://docs.qualcomm.com/doc/80-N2040-61) +//! +//! ## Vector Types +//! +//! In {bytes}-byte mode: +//! - `HvxVector` is {bits} bits ({bytes} bytes) containing {lanes} x 32-bit values +//! - `HvxVectorPair` is {pair_bits} bits ({pair_bytes} bytes) +//! - `HvxVectorPred` is {bits} bits ({bytes} bytes) for predicate operations +//! +//! To use this module, compile with `-C target-feature=+{target_feature}`. +//! +//! ## Naming Convention +//! +//! Function names preserve the original Q6 naming case because the convention +//! uses case to distinguish register types: +//! - `W` (uppercase) = vector pair (`HvxVectorPair`) +//! - `V` (uppercase) = vector (`HvxVector`) +//! - `Q` (uppercase) = predicate (`HvxVectorPred`) +//! - `R` = scalar register (`i32`) +//! +//! For example, `Q6_W_vcombine_VV` operates on a vector pair while +//! `Q6_V_hi_W` extracts a vector from a pair. +//! +//! ## Architecture Versions +//! +//! Different intrinsics require different HVX architecture versions. Use the +//! appropriate target feature to enable the required version: +//! - HVX v60: `-C target-feature=+hvxv60` (basic HVX operations) +//! - HVX v62: `-C target-feature=+hvxv62` +//! - HVX v65: `-C target-feature=+hvxv65` (includes floating-point support) +//! - HVX v66: `-C target-feature=+hvxv66` +//! - HVX v68: `-C target-feature=+hvxv68` +//! - HVX v69: `-C target-feature=+hvxv69` +//! - HVX v73: `-C target-feature=+hvxv73` +//! - HVX v79: `-C target-feature=+hvxv79` +//! +//! Each version includes all features from previous versions. +"#, + bytes = mode.bytes(), + bits = mode.bits(), + lanes = mode.lanes(), + pair_bytes = mode.bytes() * 2, + pair_bits = mode.bits() * 2, + target_feature = mode.target_feature(), + ) +} + +/// Generate the type definitions for a specific vector mode +fn generate_types(mode: VectorMode) -> String { + let lanes = mode.lanes(); + let pair_lanes = lanes * 2; + let bits = mode.bits(); + let bytes = mode.bytes(); + let pair_bits = bits * 2; + let pair_bytes = bytes * 2; + + format!( + r#" +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +#[cfg(test)] +use stdarch_test::assert_instr; + +use crate::intrinsics::simd::{{simd_add, simd_and, simd_or, simd_sub, simd_xor}}; + +// HVX type definitions for {bytes}-byte vector mode +types! {{ + #![unstable(feature = "stdarch_hexagon", issue = "{TRACKING_ISSUE}")] + + /// HVX vector type ({bits} bits / {bytes} bytes) + /// + /// This type represents a single HVX vector register containing {lanes} x 32-bit values. + pub struct HvxVector({lanes} x i32); + + /// HVX vector pair type ({pair_bits} bits / {pair_bytes} bytes) + /// + /// This type represents a pair of HVX vector registers, often used for + /// operations that produce double-width results. + pub struct HvxVectorPair({pair_lanes} x i32); + + /// HVX vector predicate type ({bits} bits / {bytes} bytes) + /// + /// This type represents a predicate vector used for conditional operations. + /// Each bit corresponds to a lane in the vector. + pub struct HvxVectorPred({lanes} x i32); +}} +"#, + bytes = bytes, + bits = bits, + lanes = lanes, + pair_bits = pair_bits, + pair_bytes = pair_bytes, + pair_lanes = pair_lanes, + TRACKING_ISSUE = TRACKING_ISSUE, + ) +} + +/// Builtin signature information for extern declarations +struct BuiltinSignature { + /// The V6_ prefixed name + full_name: String, + /// The short name (without V6_) + short_name: String, + /// Return type + return_type: RustType, + /// Parameter types + param_types: Vec, +} + +/// Get known signatures for builtins used in compound operations +/// These are the helper builtins that don't have their own Q6_ wrapper +fn get_compound_helper_signatures() -> HashMap { + let mut map = HashMap::new(); + + // vandvrt: HVX_Vector -> i32 -> HVX_Vector + // Converts predicate to vector representation. LLVM uses HVX_Vector for both. + map.insert( + "vandvrt".to_string(), + BuiltinSignature { + full_name: "V6_vandvrt".to_string(), + short_name: "vandvrt".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::I32], + }, + ); + + // vandqrt: HVX_Vector -> i32 -> HVX_Vector + // Converts vector representation back to predicate. LLVM uses HVX_Vector for both. + map.insert( + "vandqrt".to_string(), + BuiltinSignature { + full_name: "V6_vandqrt".to_string(), + short_name: "vandqrt".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::I32], + }, + ); + + // vandvrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector + map.insert( + "vandvrt_acc".to_string(), + BuiltinSignature { + full_name: "V6_vandvrt_acc".to_string(), + short_name: "vandvrt_acc".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], + }, + ); + + // vandqrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector + map.insert( + "vandqrt_acc".to_string(), + BuiltinSignature { + full_name: "V6_vandqrt_acc".to_string(), + short_name: "vandqrt_acc".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], + }, + ); + + // pred_and: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_and".to_string(), + BuiltinSignature { + full_name: "V6_pred_and".to_string(), + short_name: "pred_and".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_and_n: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_and_n".to_string(), + BuiltinSignature { + full_name: "V6_pred_and_n".to_string(), + short_name: "pred_and_n".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_or: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_or".to_string(), + BuiltinSignature { + full_name: "V6_pred_or".to_string(), + short_name: "pred_or".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_or_n: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_or_n".to_string(), + BuiltinSignature { + full_name: "V6_pred_or_n".to_string(), + short_name: "pred_or_n".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_xor: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_xor".to_string(), + BuiltinSignature { + full_name: "V6_pred_xor".to_string(), + short_name: "pred_xor".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_not: HVX_Vector -> HVX_Vector + map.insert( + "pred_not".to_string(), + BuiltinSignature { + full_name: "V6_pred_not".to_string(), + short_name: "pred_not".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector], + }, + ); + + // pred_scalar2: i32 -> HVX_Vector + map.insert( + "pred_scalar2".to_string(), + BuiltinSignature { + full_name: "V6_pred_scalar2".to_string(), + short_name: "pred_scalar2".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::I32], + }, + ); + + // Conditional store operations + map.insert( + "vS32b_qpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_qpred_ai".to_string(), + short_name: "vS32b_qpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vS32b_nqpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_nqpred_ai".to_string(), + short_name: "vS32b_nqpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vS32b_nt_qpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_nt_qpred_ai".to_string(), + short_name: "vS32b_nt_qpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vS32b_nt_nqpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_nt_nqpred_ai".to_string(), + short_name: "vS32b_nt_nqpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + // Conditional accumulation operations + for (suffix, _elem) in [("b", "byte"), ("h", "halfword"), ("w", "word")] { + // vaddbq, vaddhq, vaddwq + map.insert( + format!("vadd{}q", suffix), + BuiltinSignature { + full_name: format!("V6_vadd{}q", suffix), + short_name: format!("vadd{}q", suffix), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + // vaddbnq, vaddhnq, vaddwnq + map.insert( + format!("vadd{}nq", suffix), + BuiltinSignature { + full_name: format!("V6_vadd{}nq", suffix), + short_name: format!("vadd{}nq", suffix), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + + // Comparison operations with accumulation + // veqb_and, veqb_or, veqb_xor, etc. + for elem in ["b", "h", "w", "ub", "uh", "uw"] { + for op in ["and", "or", "xor"] { + // veq*_and, veq*_or, veq*_xor + map.insert( + format!("veq{}_{}", elem, op), + BuiltinSignature { + full_name: format!("V6_veq{}_{}", elem, op), + short_name: format!("veq{}_{}", elem, op), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + // vgt*_and, vgt*_or, vgt*_xor + map.insert( + format!("vgt{}_{}", elem, op), + BuiltinSignature { + full_name: format!("V6_vgt{}_{}", elem, op), + short_name: format!("vgt{}_{}", elem, op), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + } + + // Floating-point comparison operations (hf = half-float, sf = single-float) + for elem in ["hf", "sf"] { + // Basic comparison: vgt* + map.insert( + format!("vgt{}", elem), + BuiltinSignature { + full_name: format!("V6_vgt{}", elem), + short_name: format!("vgt{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + for op in ["and", "or", "xor"] { + // vgt*_and, vgt*_or, vgt*_xor + map.insert( + format!("vgt{}_{}", elem, op), + BuiltinSignature { + full_name: format!("V6_vgt{}_{}", elem, op), + short_name: format!("vgt{}_{}", elem, op), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + } + + // Prefix operations with predicate + for elem in ["b", "h", "w"] { + map.insert( + format!("vprefixq{}", elem), + BuiltinSignature { + full_name: format!("V6_vprefixq{}", elem), + short_name: format!("vprefixq{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector], + }, + ); + } + + // Scatter operations with predicate + map.insert( + "vscattermhq".to_string(), + BuiltinSignature { + full_name: "V6_vscattermhq".to_string(), + short_name: "vscattermhq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vscattermhwq".to_string(), + BuiltinSignature { + full_name: "V6_vscattermhwq".to_string(), + short_name: "vscattermhwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVectorPair, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vscattermwq".to_string(), + BuiltinSignature { + full_name: "V6_vscattermwq".to_string(), + short_name: "vscattermwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // Add with carry saturation + map.insert( + "vaddcarrysat".to_string(), + BuiltinSignature { + full_name: "V6_vaddcarrysat".to_string(), + short_name: "vaddcarrysat".to_string(), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // Gather operations with predicate + map.insert( + "vgathermhq".to_string(), + BuiltinSignature { + full_name: "V6_vgathermhq".to_string(), + short_name: "vgathermhq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::MutPtrHvxVector, + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vgathermhwq".to_string(), + BuiltinSignature { + full_name: "V6_vgathermhwq".to_string(), + short_name: "vgathermhwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::MutPtrHvxVector, + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVectorPair, + ], + }, + ); + + map.insert( + "vgathermwq".to_string(), + BuiltinSignature { + full_name: "V6_vgathermwq".to_string(), + short_name: "vgathermwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::MutPtrHvxVector, + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + ], + }, + ); + + // Basic comparison operations (without accumulation) + for elem in ["b", "h", "w", "ub", "uh", "uw"] { + // vgt* - greater than + map.insert( + format!("vgt{}", elem), + BuiltinSignature { + full_name: format!("V6_vgt{}", elem), + short_name: format!("vgt{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + // veq* - equal + map.insert( + format!("veq{}", elem), + BuiltinSignature { + full_name: format!("V6_veq{}", elem), + short_name: format!("veq{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + } + + // Conditional subtraction operations (vsub*q, vsub*nq) + for elem in ["b", "h", "w"] { + map.insert( + format!("vsub{}q", elem), + BuiltinSignature { + full_name: format!("V6_vsub{}q", elem), + short_name: format!("vsub{}q", elem), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + map.insert( + format!("vsub{}nq", elem), + BuiltinSignature { + full_name: format!("V6_vsub{}nq", elem), + short_name: format!("vsub{}nq", elem), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + + // vmux - vector mux (select based on predicate) + map.insert( + "vmux".to_string(), + BuiltinSignature { + full_name: "V6_vmux".to_string(), + short_name: "vmux".to_string(), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // vswap - vector swap based on predicate + map.insert( + "vswap".to_string(), + BuiltinSignature { + full_name: "V6_vswap".to_string(), + short_name: "vswap".to_string(), + return_type: RustType::HvxVectorPair, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // shuffeq operations - take vectors (internal pred representation) and return vector + for elem in ["h", "w"] { + map.insert( + format!("shuffeq{}", elem), + BuiltinSignature { + full_name: format!("V6_shuffeq{}", elem), + short_name: format!("shuffeq{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + } + + // Predicate AND with vector operations + map.insert( + "vandvqv".to_string(), + BuiltinSignature { + full_name: "V6_vandvqv".to_string(), + short_name: "vandvqv".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + map.insert( + "vandvnqv".to_string(), + BuiltinSignature { + full_name: "V6_vandvnqv".to_string(), + short_name: "vandvnqv".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // vandnqrt and vandnqrt_acc + map.insert( + "vandnqrt".to_string(), + BuiltinSignature { + full_name: "V6_vandnqrt".to_string(), + short_name: "vandnqrt".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::I32], + }, + ); + + map.insert( + "vandnqrt_acc".to_string(), + BuiltinSignature { + full_name: "V6_vandnqrt_acc".to_string(), + short_name: "vandnqrt_acc".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], + }, + ); + + // pred_scalar2v2 + map.insert( + "pred_scalar2v2".to_string(), + BuiltinSignature { + full_name: "V6_pred_scalar2v2".to_string(), + short_name: "pred_scalar2v2".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::I32], + }, + ); + + map +} + +/// Generate extern declarations for all intrinsics for a specific vector mode +fn generate_extern_block(intrinsics: &[IntrinsicInfo], mode: VectorMode) -> String { + let mut output = String::new(); + + // Collect unique builtins to avoid duplicates + let mut seen_builtins: HashSet = HashSet::new(); + let mut decls: Vec<(String, String, RustType, Vec)> = Vec::new(); + + // First, add simple intrinsics + for info in intrinsics.iter().filter(|i| !i.is_compound) { + if seen_builtins.contains(&info.builtin_name) { + continue; + } + seen_builtins.insert(info.builtin_name.clone()); + + let param_types: Vec = info.params.iter().map(|(_, t)| t.clone()).collect(); + decls.push(( + info.builtin_name.clone(), + info.instr_name.clone(), + info.return_type.clone(), + param_types, + )); + } + + // Then, collect all builtins used in compound expressions + let helper_sigs = get_compound_helper_signatures(); + let mut compound_builtins: HashSet = HashSet::new(); + + for info in intrinsics.iter().filter(|i| i.is_compound) { + if let Some(ref expr) = info.compound_expr { + collect_builtins_from_expr(expr, &mut compound_builtins); + } + } + + // Add compound helper builtins + let mut missing_builtins = Vec::new(); + for builtin_name in compound_builtins { + let full_name = format!("V6_{}", builtin_name); + if seen_builtins.contains(&full_name) { + continue; + } + seen_builtins.insert(full_name.clone()); + + if let Some(sig) = helper_sigs.get(&builtin_name) { + decls.push(( + sig.full_name.clone(), + sig.short_name.clone(), + sig.return_type.clone(), + sig.param_types.clone(), + )); + } else { + missing_builtins.push(builtin_name); + } + } + + // Report missing builtins (for development purposes) + if !missing_builtins.is_empty() { + eprintln!("Warning: Missing helper signatures for compound builtins:"); + for name in &missing_builtins { + eprintln!(" - {}", name); + } + } + + // Sort by builtin name for consistent output + decls.sort_by(|a, b| a.0.cmp(&b.0)); + + // Generate intrinsic declarations for the specified mode + output.push_str(&format!( + "// LLVM intrinsic declarations for {}-byte vector mode\n", + mode.bytes() + )); + output.push_str("#[allow(improper_ctypes)]\n"); + output.push_str("unsafe extern \"unadjusted\" {\n"); + + for (builtin_name, instr_name, return_type, param_types) in &decls { + let base_link = builtin_name.replace('_', "."); + // 128-byte mode uses .128B suffix, 64-byte mode doesn't + let link_name = if builtin_name.starts_with("V6_") && mode == VectorMode::V128 { + format!("llvm.hexagon.{}.128B", base_link) + } else { + format!("llvm.hexagon.{}", base_link) + }; + + let params_str = if param_types.is_empty() { + String::new() + } else { + param_types + .iter() + .map(|t| format!("_: {}", t.to_extern_str())) + .collect::>() + .join(", ") + }; + + let return_str = if *return_type == RustType::Unit { + " -> ()".to_string() + } else { + format!(" -> {}", return_type.to_extern_str()) + }; + + output.push_str(&format!( + " #[link_name = \"{}\"]\n fn {}({}){};\n", + link_name, instr_name, params_str, return_str + )); + } + + output.push_str("}\n"); + output +} + +/// Generate Rust code for a compound expression +/// `params` maps parameter names to their types in the function signature +/// Get the type of an expression +fn get_expr_type( + expr: &CompoundExpr, + params: &HashMap, + helper_sigs: &HashMap, +) -> Option { + match expr { + CompoundExpr::BuiltinCall(name, _) => { + helper_sigs.get(name).map(|sig| sig.return_type.clone()) + } + CompoundExpr::Param(name) => params.get(name).cloned(), + CompoundExpr::IntLiteral(_) => Some(RustType::I32), + } +} + +fn generate_compound_expr_code( + expr: &CompoundExpr, + params: &HashMap, + helper_sigs: &HashMap, +) -> String { + match expr { + CompoundExpr::BuiltinCall(name, args) => { + // Get the expected parameter types for this builtin + let expected_types = helper_sigs + .get(name) + .map(|sig| sig.param_types.clone()) + .unwrap_or_default(); + + let args_code: Vec = args + .iter() + .enumerate() + .map(|(i, arg)| { + let arg_code = generate_compound_expr_code(arg, params, helper_sigs); + + // Check if we need to transmute this argument + let expected_type = expected_types.get(i); + let actual_type = get_expr_type(arg, params, helper_sigs); + + // If the builtin expects HvxVector but the arg is HvxVectorPred, transmute + if expected_type == Some(&RustType::HvxVector) + && actual_type == Some(RustType::HvxVectorPred) + { + format!( + "core::mem::transmute::({})", + arg_code + ) + } else { + arg_code + } + }) + .collect(); + format!("{}({})", name, args_code.join(", ")) + } + CompoundExpr::Param(name) => name.clone(), + CompoundExpr::IntLiteral(n) => n.to_string(), + } +} + +/// Get the primary instruction name from a compound expression (innermost significant op) +fn get_compound_primary_instr(expr: &CompoundExpr) -> Option { + match expr { + CompoundExpr::BuiltinCall(name, args) => { + // For vandqrt wrapper, look inside + if name == "vandqrt" && !args.is_empty() { + if let Some(inner) = get_compound_primary_instr(&args[0]) { + return Some(inner); + } + } + // For store operations, use the store name + if name.starts_with("vS32b") { + return Some(name.clone()); + } + // For conditional accumulation, use the add name + if name.starts_with("vadd") && (name.ends_with("q") || name.ends_with("nq")) { + return Some(name.clone()); + } + // For predicate operations + if name.starts_with("pred_") { + return Some(name.clone()); + } + // For comparison operations with accumulation + if (name.starts_with("veq") || name.starts_with("vgt")) + && (name.ends_with("_and") || name.ends_with("_or") || name.ends_with("_xor")) + { + return Some(name.clone()); + } + Some(name.clone()) + } + _ => None, + } +} + +/// Get override implementations for specific compound intrinsics. +/// Some C macros rely on implicit type conversions that don't work with +/// our stricter Rust types, so we provide corrected implementations. +fn get_compound_overrides() -> HashMap<&'static str, &'static str> { + let mut map = HashMap::new(); + + // Q6_V_vand_QR: takes pred, returns vec + // Use transmute to convert pred to vec for LLVM, call vandvrt + map.insert( + "Q6_V_vand_QR", + "vandvrt(core::mem::transmute::(qu), rt)", + ); + + // Q6_V_vandor_VQR: takes vec and pred, returns vec + map.insert( + "Q6_V_vandor_VQR", + "vandvrt_acc(vx, core::mem::transmute::(qu), rt)", + ); + + // Q6_Q_vand_VR: takes vec, returns pred + map.insert( + "Q6_Q_vand_VR", + "core::mem::transmute::(vandqrt(vu, rt))", + ); + + // Q6_Q_vandor_QVR: takes pred and vec, returns pred + map.insert( + "Q6_Q_vandor_QVR", + "core::mem::transmute::(vandqrt_acc(core::mem::transmute::(qx), vu, rt))", + ); + + map +} + +/// Generate wrapper functions for all intrinsics +fn generate_functions(intrinsics: &[IntrinsicInfo]) -> String { + let mut output = String::new(); + let simd_mappings = get_simd_intrinsic_mappings(); + + // Generate simple intrinsics + for info in intrinsics.iter().filter(|i| !i.is_compound) { + let rust_name = &info.q6_name; + + // Generate doc comment + output.push_str(&format!("/// `{}`\n", info.asm_syntax)); + output.push_str("///\n"); + output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); + output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); + + // Generate attributes + output.push_str("#[inline]\n"); + output.push_str(&format!( + "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", + info.min_arch + )); + + // Check if we should use simd intrinsic instead + let use_simd = simd_mappings.get(info.instr_name.as_str()); + + // assert_instr uses the original instruction name + output.push_str(&format!( + "#[cfg_attr(test, assert_instr({}))]\n", + info.instr_name + )); + + output.push_str(&format!( + "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", + TRACKING_ISSUE + )); + + // Generate function signature + let params_str = info + .params + .iter() + .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) + .collect::>() + .join(", "); + + let return_str = if info.return_type == RustType::Unit { + String::new() + } else { + format!(" -> {}", info.return_type.to_rust_str()) + }; + + output.push_str(&format!( + "pub unsafe fn {}({}){} {{\n", + rust_name, params_str, return_str + )); + + // Generate function body + let args_str = info + .params + .iter() + .map(|(name, _)| name.as_str()) + .collect::>() + .join(", "); + + if let Some(simd_fn) = use_simd { + // Use architecture-independent simd intrinsic + output.push_str(&format!(" {}({})\n", simd_fn, args_str)); + } else { + // Use the LLVM intrinsic + output.push_str(&format!(" {}({})\n", info.instr_name, args_str)); + } + + output.push_str("}\n\n"); + } + + // Generate compound intrinsics + let helper_sigs = get_compound_helper_signatures(); + let overrides = get_compound_overrides(); + for info in intrinsics.iter().filter(|i| i.is_compound) { + if let Some(ref compound_expr) = info.compound_expr { + let rust_name = &info.q6_name; + + // Get the primary instruction for assert_instr + let _primary_instr = get_compound_primary_instr(compound_expr) + .unwrap_or_else(|| info.instr_name.clone()); + + // Generate doc comment + output.push_str(&format!("/// `{}`\n", info.asm_syntax)); + output.push_str("///\n"); + output.push_str( + "/// This is a compound operation composed of multiple HVX instructions.\n", + ); + if !info.instr_type.is_empty() { + output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); + } + if !info.exec_slots.is_empty() { + output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); + } + + // Generate attributes + output.push_str("#[inline]\n"); + output.push_str(&format!( + "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", + info.min_arch + )); + + // For compound ops, we skip assert_instr since they emit multiple instructions + // output.push_str(&format!( + // "#[cfg_attr(test, assert_instr({}))]\n", + // primary_instr + // )); + + output.push_str(&format!( + "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", + TRACKING_ISSUE + )); + + // Generate function signature + let params_str = info + .params + .iter() + .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) + .collect::>() + .join(", "); + + let return_str = if info.return_type == RustType::Unit { + String::new() + } else { + format!(" -> {}", info.return_type.to_rust_str()) + }; + + output.push_str(&format!( + "pub unsafe fn {}({}){} {{\n", + rust_name, params_str, return_str + )); + + // Check if we have an override for this intrinsic + let body = if let Some(override_body) = overrides.get(info.q6_name.as_str()) { + override_body.to_string() + } else { + // Build param type map for expression code generation + let param_types: HashMap = info.params.iter().cloned().collect(); + // Generate function body from compound expression + let expr_body = + generate_compound_expr_code(compound_expr, ¶m_types, &helper_sigs); + + // Check if we need to transmute the result + let expr_return_type = get_expr_type(compound_expr, ¶m_types, &helper_sigs); + if info.return_type == RustType::HvxVectorPred + && expr_return_type == Some(RustType::HvxVector) + { + format!( + "core::mem::transmute::({})", + expr_body + ) + } else { + expr_body + } + }; + output.push_str(&format!(" {}\n", body)); + + output.push_str("}\n\n"); + } + } + + output +} + +/// Generate a module file for a specific vector mode +fn generate_module_file( + intrinsics: &[IntrinsicInfo], + output_path: &Path, + mode: VectorMode, +) -> Result<(), String> { + let mut output = + File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_functions(intrinsics)).map_err(|e| e.to_string())?; + + // Ensure file is flushed before running rustfmt + drop(output); + + // Run rustfmt on the generated file + let status = std::process::Command::new("rustfmt") + .arg(output_path) + .status() + .map_err(|e| format!("Failed to run rustfmt: {}", e))?; + + if !status.success() { + return Err("rustfmt failed".to_string()); + } + + Ok(()) +} + +/// Parse the HVX header in `crate_dir` and write `v64.rs` and `v128.rs` into `out_dir`. +pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Result<(), String> { + let header_content = read_header(crate_dir)?; + let all_intrinsics = parse_header(&header_content); + let intrinsics: Vec<_> = all_intrinsics + .into_iter() + .filter(|i| i.min_arch <= MAX_SUPPORTED_ARCH) + .collect(); + for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { + let path = out_dir.join(filename); + generate_module_file(&intrinsics, &path, vmode)?; + } + Ok(()) +} diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index c3ad153ab0a7a..88ef4fe11f332 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -1,1728 +1,35 @@ -//! Hexagon HVX Code Generator +//! Hexagon code generator. //! -//! This generator creates v64.rs and v128.rs from scratch using the LLVM HVX -//! header file as the sole source of truth. It parses the C intrinsic prototypes -//! and generates Rust wrapper functions with appropriate attributes. +//! Single binary that produces every generated file under +//! `core_arch/src/hexagon/`: scalar.rs (scalar intrinsics) and +//! v64.rs / v128.rs (HVX intrinsics). //! -//! The two generated files provide: -//! - v64.rs: 64-byte vector mode intrinsics (512-bit vectors) -//! - v128.rs: 128-byte vector mode intrinsics (1024-bit vectors) -//! -//! Both modules are available unconditionally, but require the appropriate -//! target features to actually use the intrinsics. -//! -//! Usage: -//! cd crates/stdarch-gen-hexagon -//! cargo run -//! # Output is written to ../core_arch/src/hexagon/v64.rs and v128.rs - -use regex::Regex; -use std::collections::{HashMap, HashSet}; -use std::fs::File; -use std::io::Write; -use std::path::Path; -use stdarch_gen_common::{run_generator, Mode, GENERATED_MARKER}; - -/// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. -/// These intrinsics have equivalent semantics and can be lowered to the generic form. -fn get_simd_intrinsic_mappings() -> HashMap<&'static str, &'static str> { - let mut map = HashMap::new(); - // Bitwise operations (element-size independent) - map.insert("vxor", "simd_xor"); - map.insert("vand", "simd_and"); - map.insert("vor", "simd_or"); - // Word (32-bit) arithmetic operations - map.insert("vaddw", "simd_add"); - map.insert("vsubw", "simd_sub"); - map -} - -/// The tracking issue number for the stdarch_hexagon feature -const TRACKING_ISSUE: &str = "151523"; - -/// HVX vector length mode -#[derive(Debug, Clone, Copy, PartialEq)] -enum VectorMode { - /// 64-byte vectors (512 bits) - V64, - /// 128-byte vectors (1024 bits) - V128, -} - -impl VectorMode { - fn bytes(&self) -> u32 { - match self { - VectorMode::V64 => 64, - VectorMode::V128 => 128, - } - } - - fn bits(&self) -> u32 { - self.bytes() * 8 - } - - fn lanes(&self) -> u32 { - self.bytes() / 4 // 32-bit lanes - } - - fn target_feature(&self) -> &'static str { - match self { - VectorMode::V64 => "hvx-length64b", - VectorMode::V128 => "hvx-length128b", - } - } -} - -/// LLVM version the header file is from (for reference) -/// Source: https://github.com/llvm/llvm-project/blob/llvmorg-22.1.0-rc1/clang/lib/Headers/hvx_hexagon_protos.h -const LLVM_VERSION: &str = "22.1.0-rc1"; - -/// Maximum HVX architecture version supported by rustc -/// Check with: rustc --target=hexagon-unknown-linux-musl --print target-features -const MAX_SUPPORTED_ARCH: u32 = 79; - -/// Local header file path (checked into the repository) -const HEADER_FILE: &str = "hvx_hexagon_protos.h"; - -/// Intrinsic information parsed from the LLVM header -#[derive(Debug, Clone)] -struct IntrinsicInfo { - /// The Q6_* intrinsic name (e.g., "Q6_V_vadd_VV") - q6_name: String, - /// The LLVM builtin name without prefix (e.g., "V6_vaddb") - builtin_name: String, - /// The short instruction name for assert_instr (e.g., "vaddb") - instr_name: String, - /// The assembly syntax from the comment - asm_syntax: String, - /// Instruction type - instr_type: String, - /// Execution slots - exec_slots: String, - /// Minimum HVX architecture version required - min_arch: u32, - /// Return type - return_type: RustType, - /// Parameters (name, type) - params: Vec<(String, RustType)>, - /// Whether this is a compound intrinsic (multiple builtins) - is_compound: bool, - /// For compound intrinsics: the parsed expression tree - compound_expr: Option, -} - -/// Expression tree for compound intrinsics -#[derive(Debug, Clone)] -enum CompoundExpr { - /// A call to a builtin: (builtin_name without V6_ prefix, arguments) - BuiltinCall(String, Vec), - /// A parameter reference by name - Param(String), - /// An integer literal (like -1) - IntLiteral(i32), -} - -/// Rust type mappings -#[derive(Debug, Clone, PartialEq)] -enum RustType { - HvxVector, - HvxVectorPair, - HvxVectorPred, - I32, - MutPtrHvxVector, - Unit, -} - -impl RustType { - fn from_c_type(c_type: &str) -> Option { - match c_type.trim() { - "HVX_Vector" => Some(RustType::HvxVector), - "HVX_VectorPair" => Some(RustType::HvxVectorPair), - "HVX_VectorPred" => Some(RustType::HvxVectorPred), - "Word32" => Some(RustType::I32), - "HVX_Vector*" => Some(RustType::MutPtrHvxVector), - "void" => Some(RustType::Unit), - _ => None, - } - } - - fn to_rust_str(&self) -> &'static str { - match self { - RustType::HvxVector => "HvxVector", - RustType::HvxVectorPair => "HvxVectorPair", - RustType::HvxVectorPred => "HvxVectorPred", - RustType::I32 => "i32", - RustType::MutPtrHvxVector => "*mut HvxVector", - RustType::Unit => "()", - } - } - - fn to_extern_str(&self) -> &'static str { - match self { - RustType::HvxVector => "HvxVector", - RustType::HvxVectorPair => "HvxVectorPair", - RustType::HvxVectorPred => "HvxVectorPred", - RustType::I32 => "i32", - RustType::MutPtrHvxVector => "*mut HvxVector", - RustType::Unit => "()", - } - } -} - -/// Parse a compound macro expression into an expression tree -fn parse_compound_expr(expr: &str) -> Option { - let expr = expr.trim(); - - // Try to match an integer literal (like -1) - if let Ok(n) = expr.parse::() { - return Some(CompoundExpr::IntLiteral(n)); - } - - // Try to match a simple parameter name (Vu, Vv, Rt, Qs, Qt, Qx, Vx, etc.) - // These are typically short identifiers in the macro - if expr.len() <= 3 - && expr.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') - && !expr.contains("__") - { - return Some(CompoundExpr::Param(expr.to_lowercase())); - } - - // Check if it's wrapped in extra parens first - if expr.starts_with('(') && expr.ends_with(')') { - // Check if these parens wrap the entire expression - let inner = &expr[1..expr.len() - 1]; - // Count depth: if after removing outer parens the expression is balanced, - // the outer parens were enclosing everything - if is_balanced_parens(inner) { - // But we also need to verify these aren't part of a function call - // If the inner expression is balanced and the whole thing starts with ( - // and ends with ), it's a paren wrapper - let result = parse_compound_expr(inner); - if result.is_some() { - return result; - } - } - } - - // Try to match __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) - // The args portion may contain nested calls, so we need to find the matching paren - if expr.starts_with("__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_") { - // Find the end of the builtin name (after V6_) - let prefix = "__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_"; - let after_prefix = &expr[prefix.len()..]; - if let Some(paren_pos) = after_prefix.find(')') { - let builtin_name = &after_prefix[..paren_pos]; - let rest = &after_prefix[paren_pos + 1..]; // Skip the closing ) of the WRAP - // rest should now be "(args)" - if rest.starts_with('(') && rest.ends_with(')') { - let args_str = &rest[1..rest.len() - 1]; - let args = parse_compound_args(args_str)?; - return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); - } - } - } - - // Try to match __builtin_HEXAGON_V6_xxx(args) without wrap - if expr.starts_with("__builtin_HEXAGON_V6_") { - let prefix = "__builtin_HEXAGON_V6_"; - let after_prefix = &expr[prefix.len()..]; - if let Some(paren_pos) = after_prefix.find('(') { - let builtin_name = &after_prefix[..paren_pos]; - let rest = &after_prefix[paren_pos..]; - if rest.starts_with('(') && rest.ends_with(')') { - let args_str = &rest[1..rest.len() - 1]; - let args = parse_compound_args(args_str)?; - return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); - } - } - } - - None -} - -/// Check if parentheses are balanced in a string -fn is_balanced_parens(s: &str) -> bool { - let mut depth = 0; - for c in s.chars() { - match c { - '(' => depth += 1, - ')' => { - depth -= 1; - if depth < 0 { - return false; - } - } - _ => {} - } - } - depth == 0 -} - -/// Parse comma-separated arguments, respecting nested parentheses -fn parse_compound_args(args_str: &str) -> Option> { - let mut args = Vec::new(); - let mut current = String::new(); - let mut depth = 0; - - for c in args_str.chars() { - match c { - '(' => { - depth += 1; - current.push(c); - } - ')' => { - depth -= 1; - current.push(c); - } - ',' if depth == 0 => { - let arg = current.trim().to_string(); - if !arg.is_empty() { - args.push(parse_compound_expr(&arg)?); - } - current.clear(); - } - _ => current.push(c), - } - } - - // Don't forget the last argument - let arg = current.trim().to_string(); - if !arg.is_empty() { - args.push(parse_compound_expr(&arg)?); - } - - Some(args) -} - -/// Extract all builtin names used in a compound expression -fn collect_builtins_from_expr(expr: &CompoundExpr, builtins: &mut HashSet) { - match expr { - CompoundExpr::BuiltinCall(name, args) => { - builtins.insert(name.clone()); - for arg in args { - collect_builtins_from_expr(arg, builtins); - } - } - CompoundExpr::Param(_) | CompoundExpr::IntLiteral(_) => {} - } -} - -/// Read the local HVX header file -fn read_header(crate_dir: &Path) -> Result { - let header_path = crate_dir.join(HEADER_FILE); - println!("Reading HVX header from: {}", header_path.display()); - println!(" (LLVM version: {})", LLVM_VERSION); - - std::fs::read_to_string(&header_path).map_err(|e| { - format!( - "Failed to read header file {}: {}", - header_path.display(), - e - ) - }) -} - -/// Parse a C function prototype to extract return type and parameters -fn parse_prototype(prototype: &str) -> Option<(RustType, Vec<(String, RustType)>)> { - // Pattern: ReturnType FunctionName(ParamType1 Param1, ParamType2 Param2, ...) - let proto_re = Regex::new(r"(\w+(?:\*)?)\s+Q6_\w+\(([^)]*)\)").unwrap(); - - if let Some(caps) = proto_re.captures(prototype) { - let return_type_str = caps[1].trim(); - let params_str = &caps[2]; - - let return_type = RustType::from_c_type(return_type_str)?; - - let mut params = Vec::new(); - if !params_str.trim().is_empty() { - // Pattern: Type Name or Type* Name - let param_re = Regex::new(r"(\w+\*?)\s+(\w+)").unwrap(); - for param in params_str.split(',') { - let param = param.trim(); - if let Some(pcaps) = param_re.captures(param) { - let ptype_str = pcaps[1].trim(); - let pname = pcaps[2].to_lowercase(); - if let Some(ptype) = RustType::from_c_type(ptype_str) { - params.push((pname, ptype)); - } else { - return None; // Unknown type - } - } - } - } - - Some((return_type, params)) - } else { - None - } -} - -/// Parse the LLVM header file to extract intrinsic information -fn parse_header(content: &str) -> Vec { - let mut intrinsics = Vec::new(); - - let arch_re = Regex::new(r"#if __HVX_ARCH__ >= (\d+)").unwrap(); - - // Regex to extract the simple builtin name from a macro body - // Match: __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) - let simple_builtin_re = - Regex::new(r"__BUILTIN_VECTOR_WRAP\(__builtin_HEXAGON_(\w+)\)\([^)]*\)\s*$").unwrap(); - - // Also handle builtins without VECTOR_WRAP - let simple_builtin_re2 = Regex::new(r"__builtin_HEXAGON_(\w+)\([^)]*\)\s*$").unwrap(); - - // Regex to extract Q6 name from #define - let q6_name_re = Regex::new(r"#define\s+(Q6_\w+)").unwrap(); - - // Regex to extract macro expression body - let macro_expr_re = Regex::new(r"#define\s+Q6_\w+\([^)]*\)\s+(.+)").unwrap(); - - let lines: Vec<&str> = content.lines().collect(); - let mut current_arch: u32 = 60; - let mut i = 0; - - while i < lines.len() { - // Track architecture version - if let Some(caps) = arch_re.captures(lines[i]) { - if let Ok(arch) = caps[1].parse() { - current_arch = arch; - } - } - - // Look for Assembly Syntax comment block - if lines[i].contains("Assembly Syntax:") { - let mut asm_syntax = String::new(); - let mut prototype = String::new(); - let mut instr_type = String::new(); - let mut exec_slots = String::new(); - - // Parse the comment block - let mut j = i; - while j < lines.len() && !lines[j].starts_with("#define") { - let line = lines[j]; - if line.contains("Assembly Syntax:") { - if let Some(pos) = line.find("Assembly Syntax:") { - asm_syntax = line[pos + 16..].trim().to_string(); - } - } else if line.contains("C Intrinsic Prototype:") { - if let Some(pos) = line.find("C Intrinsic Prototype:") { - prototype = line[pos + 22..].trim().to_string(); - } - } else if line.contains("Instruction Type:") { - if let Some(pos) = line.find("Instruction Type:") { - instr_type = line[pos + 17..].trim().to_string(); - } - } else if line.contains("Execution Slots:") { - if let Some(pos) = line.find("Execution Slots:") { - exec_slots = line[pos + 16..].trim().to_string(); - } - } - j += 1; - } - - // Now find the #define line - while j < lines.len() && !lines[j].starts_with("#define") { - j += 1; - } - - if j < lines.len() { - let define_line = lines[j]; - - // Extract Q6 name and check if it's simple or compound - if let Some(caps) = q6_name_re.captures(define_line) { - let q6_name = caps[1].to_string(); - - // Get the full macro body (handle line continuations) - let mut macro_body = define_line.to_string(); - let mut k = j; - while macro_body.trim_end().ends_with('\\') && k + 1 < lines.len() { - k += 1; - macro_body.push_str(lines[k]); - } - - // Try to extract simple builtin name - let builtin_name = simple_builtin_re - .captures(¯o_body) - .or_else(|| simple_builtin_re2.captures(¯o_body)) - .map(|bcaps| bcaps[1].to_string()); - - // Check if it's a compound intrinsic (multiple __builtin calls) - let builtin_count = macro_body.matches("__builtin_HEXAGON_").count(); - let is_compound = builtin_count > 1; - - // Parse prototype - if let Some((return_type, params)) = parse_prototype(&prototype) { - if is_compound { - // For compound intrinsics, parse the expression - // Extract the macro body after the parameter list - if let Some(expr_caps) = macro_expr_re.captures(¯o_body) { - let expr_str = expr_caps[1].trim().replace(['\n', '\\'], " "); - let expr_str = expr_str.trim(); - - if let Some(compound_expr) = parse_compound_expr(expr_str) { - // For compound intrinsics, we use the outermost builtin - // as the "primary" for the instruction name - let (primary_builtin, instr_name) = match &compound_expr { - CompoundExpr::BuiltinCall(name, _) => { - (name.clone(), name.clone()) - } - _ => continue, - }; - - intrinsics.push(IntrinsicInfo { - q6_name, - builtin_name: format!("V6_{}", primary_builtin), - instr_name, - asm_syntax, - instr_type, - exec_slots, - min_arch: current_arch, - return_type, - params, - is_compound: true, - compound_expr: Some(compound_expr), - }); - } - } - } else if let Some(builtin) = builtin_name { - // Extract short instruction name - let instr_name = builtin - .strip_prefix("V6_") - .map(|s| s.to_string()) - .unwrap_or_else(|| builtin.clone()); - - intrinsics.push(IntrinsicInfo { - q6_name, - builtin_name: builtin, - instr_name, - asm_syntax, - instr_type, - exec_slots, - min_arch: current_arch, - return_type, - params, - is_compound: false, - compound_expr: None, - }); - } - } - } - } - i = j; - } - i += 1; - } - - intrinsics -} - -/// Generate the module documentation -fn generate_module_doc(mode: VectorMode) -> String { - format!( - r#"//! Hexagon HVX {bytes}-byte vector mode intrinsics -//! -//! This module provides intrinsics for the Hexagon Vector Extensions (HVX) -//! in {bytes}-byte vector mode ({bits}-bit vectors). -//! -//! HVX is a wide vector extension designed for high-performance signal processing. -//! [Hexagon HVX Programmer's Reference Manual](https://docs.qualcomm.com/doc/80-N2040-61) -//! -//! ## Vector Types -//! -//! In {bytes}-byte mode: -//! - `HvxVector` is {bits} bits ({bytes} bytes) containing {lanes} x 32-bit values -//! - `HvxVectorPair` is {pair_bits} bits ({pair_bytes} bytes) -//! - `HvxVectorPred` is {bits} bits ({bytes} bytes) for predicate operations -//! -//! To use this module, compile with `-C target-feature=+{target_feature}`. -//! -//! ## Naming Convention -//! -//! Function names preserve the original Q6 naming case because the convention -//! uses case to distinguish register types: -//! - `W` (uppercase) = vector pair (`HvxVectorPair`) -//! - `V` (uppercase) = vector (`HvxVector`) -//! - `Q` (uppercase) = predicate (`HvxVectorPred`) -//! - `R` = scalar register (`i32`) -//! -//! For example, `Q6_W_vcombine_VV` operates on a vector pair while -//! `Q6_V_hi_W` extracts a vector from a pair. -//! -//! ## Architecture Versions -//! -//! Different intrinsics require different HVX architecture versions. Use the -//! appropriate target feature to enable the required version: -//! - HVX v60: `-C target-feature=+hvxv60` (basic HVX operations) -//! - HVX v62: `-C target-feature=+hvxv62` -//! - HVX v65: `-C target-feature=+hvxv65` (includes floating-point support) -//! - HVX v66: `-C target-feature=+hvxv66` -//! - HVX v68: `-C target-feature=+hvxv68` -//! - HVX v69: `-C target-feature=+hvxv69` -//! - HVX v73: `-C target-feature=+hvxv73` -//! - HVX v79: `-C target-feature=+hvxv79` -//! -//! Each version includes all features from previous versions. -"#, - bytes = mode.bytes(), - bits = mode.bits(), - lanes = mode.lanes(), - pair_bytes = mode.bytes() * 2, - pair_bits = mode.bits() * 2, - target_feature = mode.target_feature(), - ) -} - -/// Generate the type definitions for a specific vector mode -fn generate_types(mode: VectorMode) -> String { - let lanes = mode.lanes(); - let pair_lanes = lanes * 2; - let bits = mode.bits(); - let bytes = mode.bytes(); - let pair_bits = bits * 2; - let pair_bytes = bytes * 2; - - format!( - r#" -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -#[cfg(test)] -use stdarch_test::assert_instr; - -use crate::intrinsics::simd::{{simd_add, simd_and, simd_or, simd_sub, simd_xor}}; - -// HVX type definitions for {bytes}-byte vector mode -types! {{ - #![unstable(feature = "stdarch_hexagon", issue = "{TRACKING_ISSUE}")] - - /// HVX vector type ({bits} bits / {bytes} bytes) - /// - /// This type represents a single HVX vector register containing {lanes} x 32-bit values. - pub struct HvxVector({lanes} x i32); - - /// HVX vector pair type ({pair_bits} bits / {pair_bytes} bytes) - /// - /// This type represents a pair of HVX vector registers, often used for - /// operations that produce double-width results. - pub struct HvxVectorPair({pair_lanes} x i32); - - /// HVX vector predicate type ({bits} bits / {bytes} bytes) - /// - /// This type represents a predicate vector used for conditional operations. - /// Each bit corresponds to a lane in the vector. - pub struct HvxVectorPred({lanes} x i32); -}} -"#, - bytes = bytes, - bits = bits, - lanes = lanes, - pair_bits = pair_bits, - pair_bytes = pair_bytes, - pair_lanes = pair_lanes, - TRACKING_ISSUE = TRACKING_ISSUE, - ) -} - -/// Builtin signature information for extern declarations -struct BuiltinSignature { - /// The V6_ prefixed name - full_name: String, - /// The short name (without V6_) - short_name: String, - /// Return type - return_type: RustType, - /// Parameter types - param_types: Vec, -} - -/// Get known signatures for builtins used in compound operations -/// These are the helper builtins that don't have their own Q6_ wrapper -fn get_compound_helper_signatures() -> HashMap { - let mut map = HashMap::new(); - - // vandvrt: HVX_Vector -> i32 -> HVX_Vector - // Converts predicate to vector representation. LLVM uses HVX_Vector for both. - map.insert( - "vandvrt".to_string(), - BuiltinSignature { - full_name: "V6_vandvrt".to_string(), - short_name: "vandvrt".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::I32], - }, - ); - - // vandqrt: HVX_Vector -> i32 -> HVX_Vector - // Converts vector representation back to predicate. LLVM uses HVX_Vector for both. - map.insert( - "vandqrt".to_string(), - BuiltinSignature { - full_name: "V6_vandqrt".to_string(), - short_name: "vandqrt".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::I32], - }, - ); - - // vandvrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector - map.insert( - "vandvrt_acc".to_string(), - BuiltinSignature { - full_name: "V6_vandvrt_acc".to_string(), - short_name: "vandvrt_acc".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], - }, - ); - - // vandqrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector - map.insert( - "vandqrt_acc".to_string(), - BuiltinSignature { - full_name: "V6_vandqrt_acc".to_string(), - short_name: "vandqrt_acc".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], - }, - ); - - // pred_and: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_and".to_string(), - BuiltinSignature { - full_name: "V6_pred_and".to_string(), - short_name: "pred_and".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // pred_and_n: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_and_n".to_string(), - BuiltinSignature { - full_name: "V6_pred_and_n".to_string(), - short_name: "pred_and_n".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // pred_or: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_or".to_string(), - BuiltinSignature { - full_name: "V6_pred_or".to_string(), - short_name: "pred_or".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // pred_or_n: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_or_n".to_string(), - BuiltinSignature { - full_name: "V6_pred_or_n".to_string(), - short_name: "pred_or_n".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); +//! Run in check or bless mode via `STDARCH_GEN_MODE`. - // pred_xor: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_xor".to_string(), - BuiltinSignature { - full_name: "V6_pred_xor".to_string(), - short_name: "pred_xor".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); +mod hvx; +mod scalar; - // pred_not: HVX_Vector -> HVX_Vector - map.insert( - "pred_not".to_string(), - BuiltinSignature { - full_name: "V6_pred_not".to_string(), - short_name: "pred_not".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector], - }, - ); - - // pred_scalar2: i32 -> HVX_Vector - map.insert( - "pred_scalar2".to_string(), - BuiltinSignature { - full_name: "V6_pred_scalar2".to_string(), - short_name: "pred_scalar2".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::I32], - }, - ); - - // Conditional store operations - map.insert( - "vS32b_qpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_qpred_ai".to_string(), - short_name: "vS32b_qpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vS32b_nqpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_nqpred_ai".to_string(), - short_name: "vS32b_nqpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vS32b_nt_qpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_nt_qpred_ai".to_string(), - short_name: "vS32b_nt_qpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vS32b_nt_nqpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_nt_nqpred_ai".to_string(), - short_name: "vS32b_nt_nqpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - // Conditional accumulation operations - for (suffix, _elem) in [("b", "byte"), ("h", "halfword"), ("w", "word")] { - // vaddbq, vaddhq, vaddwq - map.insert( - format!("vadd{}q", suffix), - BuiltinSignature { - full_name: format!("V6_vadd{}q", suffix), - short_name: format!("vadd{}q", suffix), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - // vaddbnq, vaddhnq, vaddwnq - map.insert( - format!("vadd{}nq", suffix), - BuiltinSignature { - full_name: format!("V6_vadd{}nq", suffix), - short_name: format!("vadd{}nq", suffix), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - - // Comparison operations with accumulation - // veqb_and, veqb_or, veqb_xor, etc. - for elem in ["b", "h", "w", "ub", "uh", "uw"] { - for op in ["and", "or", "xor"] { - // veq*_and, veq*_or, veq*_xor - map.insert( - format!("veq{}_{}", elem, op), - BuiltinSignature { - full_name: format!("V6_veq{}_{}", elem, op), - short_name: format!("veq{}_{}", elem, op), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - // vgt*_and, vgt*_or, vgt*_xor - map.insert( - format!("vgt{}_{}", elem, op), - BuiltinSignature { - full_name: format!("V6_vgt{}_{}", elem, op), - short_name: format!("vgt{}_{}", elem, op), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - } - - // Floating-point comparison operations (hf = half-float, sf = single-float) - for elem in ["hf", "sf"] { - // Basic comparison: vgt* - map.insert( - format!("vgt{}", elem), - BuiltinSignature { - full_name: format!("V6_vgt{}", elem), - short_name: format!("vgt{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - for op in ["and", "or", "xor"] { - // vgt*_and, vgt*_or, vgt*_xor - map.insert( - format!("vgt{}_{}", elem, op), - BuiltinSignature { - full_name: format!("V6_vgt{}_{}", elem, op), - short_name: format!("vgt{}_{}", elem, op), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - } - - // Prefix operations with predicate - for elem in ["b", "h", "w"] { - map.insert( - format!("vprefixq{}", elem), - BuiltinSignature { - full_name: format!("V6_vprefixq{}", elem), - short_name: format!("vprefixq{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector], - }, - ); - } - - // Scatter operations with predicate - map.insert( - "vscattermhq".to_string(), - BuiltinSignature { - full_name: "V6_vscattermhq".to_string(), - short_name: "vscattermhq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vscattermhwq".to_string(), - BuiltinSignature { - full_name: "V6_vscattermhwq".to_string(), - short_name: "vscattermhwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVectorPair, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vscattermwq".to_string(), - BuiltinSignature { - full_name: "V6_vscattermwq".to_string(), - short_name: "vscattermwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // Add with carry saturation - map.insert( - "vaddcarrysat".to_string(), - BuiltinSignature { - full_name: "V6_vaddcarrysat".to_string(), - short_name: "vaddcarrysat".to_string(), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // Gather operations with predicate - map.insert( - "vgathermhq".to_string(), - BuiltinSignature { - full_name: "V6_vgathermhq".to_string(), - short_name: "vgathermhq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::MutPtrHvxVector, - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vgathermhwq".to_string(), - BuiltinSignature { - full_name: "V6_vgathermhwq".to_string(), - short_name: "vgathermhwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::MutPtrHvxVector, - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVectorPair, - ], - }, - ); - - map.insert( - "vgathermwq".to_string(), - BuiltinSignature { - full_name: "V6_vgathermwq".to_string(), - short_name: "vgathermwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::MutPtrHvxVector, - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - ], - }, - ); - - // Basic comparison operations (without accumulation) - for elem in ["b", "h", "w", "ub", "uh", "uw"] { - // vgt* - greater than - map.insert( - format!("vgt{}", elem), - BuiltinSignature { - full_name: format!("V6_vgt{}", elem), - short_name: format!("vgt{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - // veq* - equal - map.insert( - format!("veq{}", elem), - BuiltinSignature { - full_name: format!("V6_veq{}", elem), - short_name: format!("veq{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - } - - // Conditional subtraction operations (vsub*q, vsub*nq) - for elem in ["b", "h", "w"] { - map.insert( - format!("vsub{}q", elem), - BuiltinSignature { - full_name: format!("V6_vsub{}q", elem), - short_name: format!("vsub{}q", elem), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - map.insert( - format!("vsub{}nq", elem), - BuiltinSignature { - full_name: format!("V6_vsub{}nq", elem), - short_name: format!("vsub{}nq", elem), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - - // vmux - vector mux (select based on predicate) - map.insert( - "vmux".to_string(), - BuiltinSignature { - full_name: "V6_vmux".to_string(), - short_name: "vmux".to_string(), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // vswap - vector swap based on predicate - map.insert( - "vswap".to_string(), - BuiltinSignature { - full_name: "V6_vswap".to_string(), - short_name: "vswap".to_string(), - return_type: RustType::HvxVectorPair, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // shuffeq operations - take vectors (internal pred representation) and return vector - for elem in ["h", "w"] { - map.insert( - format!("shuffeq{}", elem), - BuiltinSignature { - full_name: format!("V6_shuffeq{}", elem), - short_name: format!("shuffeq{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - } - - // Predicate AND with vector operations - map.insert( - "vandvqv".to_string(), - BuiltinSignature { - full_name: "V6_vandvqv".to_string(), - short_name: "vandvqv".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - map.insert( - "vandvnqv".to_string(), - BuiltinSignature { - full_name: "V6_vandvnqv".to_string(), - short_name: "vandvnqv".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // vandnqrt and vandnqrt_acc - map.insert( - "vandnqrt".to_string(), - BuiltinSignature { - full_name: "V6_vandnqrt".to_string(), - short_name: "vandnqrt".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::I32], - }, - ); - - map.insert( - "vandnqrt_acc".to_string(), - BuiltinSignature { - full_name: "V6_vandnqrt_acc".to_string(), - short_name: "vandnqrt_acc".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], - }, - ); - - // pred_scalar2v2 - map.insert( - "pred_scalar2v2".to_string(), - BuiltinSignature { - full_name: "V6_pred_scalar2v2".to_string(), - short_name: "pred_scalar2v2".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::I32], - }, - ); - - map -} - -/// Generate extern declarations for all intrinsics for a specific vector mode -fn generate_extern_block(intrinsics: &[IntrinsicInfo], mode: VectorMode) -> String { - let mut output = String::new(); - - // Collect unique builtins to avoid duplicates - let mut seen_builtins: HashSet = HashSet::new(); - let mut decls: Vec<(String, String, RustType, Vec)> = Vec::new(); - - // First, add simple intrinsics - for info in intrinsics.iter().filter(|i| !i.is_compound) { - if seen_builtins.contains(&info.builtin_name) { - continue; - } - seen_builtins.insert(info.builtin_name.clone()); - - let param_types: Vec = info.params.iter().map(|(_, t)| t.clone()).collect(); - decls.push(( - info.builtin_name.clone(), - info.instr_name.clone(), - info.return_type.clone(), - param_types, - )); - } - - // Then, collect all builtins used in compound expressions - let helper_sigs = get_compound_helper_signatures(); - let mut compound_builtins: HashSet = HashSet::new(); - - for info in intrinsics.iter().filter(|i| i.is_compound) { - if let Some(ref expr) = info.compound_expr { - collect_builtins_from_expr(expr, &mut compound_builtins); - } - } - - // Add compound helper builtins - let mut missing_builtins = Vec::new(); - for builtin_name in compound_builtins { - let full_name = format!("V6_{}", builtin_name); - if seen_builtins.contains(&full_name) { - continue; - } - seen_builtins.insert(full_name.clone()); - - if let Some(sig) = helper_sigs.get(&builtin_name) { - decls.push(( - sig.full_name.clone(), - sig.short_name.clone(), - sig.return_type.clone(), - sig.param_types.clone(), - )); - } else { - missing_builtins.push(builtin_name); - } - } - - // Report missing builtins (for development purposes) - if !missing_builtins.is_empty() { - eprintln!("Warning: Missing helper signatures for compound builtins:"); - for name in &missing_builtins { - eprintln!(" - {}", name); - } - } - - // Sort by builtin name for consistent output - decls.sort_by(|a, b| a.0.cmp(&b.0)); - - // Generate intrinsic declarations for the specified mode - output.push_str(&format!( - "// LLVM intrinsic declarations for {}-byte vector mode\n", - mode.bytes() - )); - output.push_str("#[allow(improper_ctypes)]\n"); - output.push_str("unsafe extern \"unadjusted\" {\n"); - - for (builtin_name, instr_name, return_type, param_types) in &decls { - let base_link = builtin_name.replace('_', "."); - // 128-byte mode uses .128B suffix, 64-byte mode doesn't - let link_name = if builtin_name.starts_with("V6_") && mode == VectorMode::V128 { - format!("llvm.hexagon.{}.128B", base_link) - } else { - format!("llvm.hexagon.{}", base_link) - }; - - let params_str = if param_types.is_empty() { - String::new() - } else { - param_types - .iter() - .map(|t| format!("_: {}", t.to_extern_str())) - .collect::>() - .join(", ") - }; - - let return_str = if *return_type == RustType::Unit { - " -> ()".to_string() - } else { - format!(" -> {}", return_type.to_extern_str()) - }; - - output.push_str(&format!( - " #[link_name = \"{}\"]\n fn {}({}){};\n", - link_name, instr_name, params_str, return_str - )); - } - - output.push_str("}\n"); - output -} - -/// Generate Rust code for a compound expression -/// `params` maps parameter names to their types in the function signature -/// Get the type of an expression -fn get_expr_type( - expr: &CompoundExpr, - params: &HashMap, - helper_sigs: &HashMap, -) -> Option { - match expr { - CompoundExpr::BuiltinCall(name, _) => { - helper_sigs.get(name).map(|sig| sig.return_type.clone()) - } - CompoundExpr::Param(name) => params.get(name).cloned(), - CompoundExpr::IntLiteral(_) => Some(RustType::I32), - } -} - -fn generate_compound_expr_code( - expr: &CompoundExpr, - params: &HashMap, - helper_sigs: &HashMap, -) -> String { - match expr { - CompoundExpr::BuiltinCall(name, args) => { - // Get the expected parameter types for this builtin - let expected_types = helper_sigs - .get(name) - .map(|sig| sig.param_types.clone()) - .unwrap_or_default(); - - let args_code: Vec = args - .iter() - .enumerate() - .map(|(i, arg)| { - let arg_code = generate_compound_expr_code(arg, params, helper_sigs); - - // Check if we need to transmute this argument - let expected_type = expected_types.get(i); - let actual_type = get_expr_type(arg, params, helper_sigs); - - // If the builtin expects HvxVector but the arg is HvxVectorPred, transmute - if expected_type == Some(&RustType::HvxVector) - && actual_type == Some(RustType::HvxVectorPred) - { - format!( - "core::mem::transmute::({})", - arg_code - ) - } else { - arg_code - } - }) - .collect(); - format!("{}({})", name, args_code.join(", ")) - } - CompoundExpr::Param(name) => name.clone(), - CompoundExpr::IntLiteral(n) => n.to_string(), - } -} - -/// Get the primary instruction name from a compound expression (innermost significant op) -fn get_compound_primary_instr(expr: &CompoundExpr) -> Option { - match expr { - CompoundExpr::BuiltinCall(name, args) => { - // For vandqrt wrapper, look inside - if name == "vandqrt" && !args.is_empty() { - if let Some(inner) = get_compound_primary_instr(&args[0]) { - return Some(inner); - } - } - // For store operations, use the store name - if name.starts_with("vS32b") { - return Some(name.clone()); - } - // For conditional accumulation, use the add name - if name.starts_with("vadd") && (name.ends_with("q") || name.ends_with("nq")) { - return Some(name.clone()); - } - // For predicate operations - if name.starts_with("pred_") { - return Some(name.clone()); - } - // For comparison operations with accumulation - if (name.starts_with("veq") || name.starts_with("vgt")) - && (name.ends_with("_and") || name.ends_with("_or") || name.ends_with("_xor")) - { - return Some(name.clone()); - } - Some(name.clone()) - } - _ => None, - } -} - -/// Get override implementations for specific compound intrinsics. -/// Some C macros rely on implicit type conversions that don't work with -/// our stricter Rust types, so we provide corrected implementations. -fn get_compound_overrides() -> HashMap<&'static str, &'static str> { - let mut map = HashMap::new(); - - // Q6_V_vand_QR: takes pred, returns vec - // Use transmute to convert pred to vec for LLVM, call vandvrt - map.insert( - "Q6_V_vand_QR", - "vandvrt(core::mem::transmute::(qu), rt)", - ); - - // Q6_V_vandor_VQR: takes vec and pred, returns vec - map.insert( - "Q6_V_vandor_VQR", - "vandvrt_acc(vx, core::mem::transmute::(qu), rt)", - ); - - // Q6_Q_vand_VR: takes vec, returns pred - map.insert( - "Q6_Q_vand_VR", - "core::mem::transmute::(vandqrt(vu, rt))", - ); - - // Q6_Q_vandor_QVR: takes pred and vec, returns pred - map.insert( - "Q6_Q_vandor_QVR", - "core::mem::transmute::(vandqrt_acc(core::mem::transmute::(qx), vu, rt))", - ); - - map -} - -/// Generate wrapper functions for all intrinsics -fn generate_functions(intrinsics: &[IntrinsicInfo]) -> String { - let mut output = String::new(); - let simd_mappings = get_simd_intrinsic_mappings(); - - // Generate simple intrinsics - for info in intrinsics.iter().filter(|i| !i.is_compound) { - let rust_name = &info.q6_name; - - // Generate doc comment - output.push_str(&format!("/// `{}`\n", info.asm_syntax)); - output.push_str("///\n"); - output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); - output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); - - // Generate attributes - output.push_str("#[inline]\n"); - output.push_str(&format!( - "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", - info.min_arch - )); - - // Check if we should use simd intrinsic instead - let use_simd = simd_mappings.get(info.instr_name.as_str()); - - // assert_instr uses the original instruction name - output.push_str(&format!( - "#[cfg_attr(test, assert_instr({}))]\n", - info.instr_name - )); - - output.push_str(&format!( - "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", - TRACKING_ISSUE - )); - - // Generate function signature - let params_str = info - .params - .iter() - .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) - .collect::>() - .join(", "); - - let return_str = if info.return_type == RustType::Unit { - String::new() - } else { - format!(" -> {}", info.return_type.to_rust_str()) - }; - - output.push_str(&format!( - "pub unsafe fn {}({}){} {{\n", - rust_name, params_str, return_str - )); - - // Generate function body - let args_str = info - .params - .iter() - .map(|(name, _)| name.as_str()) - .collect::>() - .join(", "); - - if let Some(simd_fn) = use_simd { - // Use architecture-independent simd intrinsic - output.push_str(&format!(" {}({})\n", simd_fn, args_str)); - } else { - // Use the LLVM intrinsic - output.push_str(&format!(" {}({})\n", info.instr_name, args_str)); - } - - output.push_str("}\n\n"); - } - - // Generate compound intrinsics - let helper_sigs = get_compound_helper_signatures(); - let overrides = get_compound_overrides(); - for info in intrinsics.iter().filter(|i| i.is_compound) { - if let Some(ref compound_expr) = info.compound_expr { - let rust_name = &info.q6_name; - - // Get the primary instruction for assert_instr - let _primary_instr = get_compound_primary_instr(compound_expr) - .unwrap_or_else(|| info.instr_name.clone()); - - // Generate doc comment - output.push_str(&format!("/// `{}`\n", info.asm_syntax)); - output.push_str("///\n"); - output.push_str( - "/// This is a compound operation composed of multiple HVX instructions.\n", - ); - if !info.instr_type.is_empty() { - output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); - } - if !info.exec_slots.is_empty() { - output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); - } - - // Generate attributes - output.push_str("#[inline]\n"); - output.push_str(&format!( - "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", - info.min_arch - )); - - // For compound ops, we skip assert_instr since they emit multiple instructions - // output.push_str(&format!( - // "#[cfg_attr(test, assert_instr({}))]\n", - // primary_instr - // )); - - output.push_str(&format!( - "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", - TRACKING_ISSUE - )); - - // Generate function signature - let params_str = info - .params - .iter() - .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) - .collect::>() - .join(", "); - - let return_str = if info.return_type == RustType::Unit { - String::new() - } else { - format!(" -> {}", info.return_type.to_rust_str()) - }; - - output.push_str(&format!( - "pub unsafe fn {}({}){} {{\n", - rust_name, params_str, return_str - )); - - // Check if we have an override for this intrinsic - let body = if let Some(override_body) = overrides.get(info.q6_name.as_str()) { - override_body.to_string() - } else { - // Build param type map for expression code generation - let param_types: HashMap = info.params.iter().cloned().collect(); - // Generate function body from compound expression - let expr_body = - generate_compound_expr_code(compound_expr, ¶m_types, &helper_sigs); - - // Check if we need to transmute the result - let expr_return_type = get_expr_type(compound_expr, ¶m_types, &helper_sigs); - if info.return_type == RustType::HvxVectorPred - && expr_return_type == Some(RustType::HvxVector) - { - format!( - "core::mem::transmute::({})", - expr_body - ) - } else { - expr_body - } - }; - output.push_str(&format!(" {}\n", body)); - - output.push_str("}\n\n"); - } - } - - output -} - -/// Generate a module file for a specific vector mode -fn generate_module_file( - intrinsics: &[IntrinsicInfo], - output_path: &Path, - mode: VectorMode, -) -> Result<(), String> { - let mut output = - File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; - - writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_functions(intrinsics)).map_err(|e| e.to_string())?; - - // Ensure file is flushed before running rustfmt - drop(output); - - // Run rustfmt on the generated file - let status = std::process::Command::new("rustfmt") - .arg(output_path) - .status() - .map_err(|e| format!("Failed to run rustfmt: {}", e))?; - - if !status.success() { - return Err("rustfmt failed".to_string()); - } - - Ok(()) -} +use std::path::PathBuf; +use stdarch_gen_common::{run_generator, Mode}; fn main() -> Result<(), String> { - println!("=== Hexagon HVX Code Generator ===\n"); - - // Get the crate directory first (needed for both reading header and writing output) let crate_dir = std::env::var("CARGO_MANIFEST_DIR") - .map(std::path::PathBuf::from) + .map(PathBuf::from) .unwrap_or_else(|_| std::env::current_dir().unwrap()); - // Read and parse the local LLVM header - println!("Step 1: Reading LLVM HVX header..."); - let header_content = read_header(&crate_dir)?; - println!(" Read {} bytes", header_content.len()); - - println!("\nStep 2: Parsing intrinsic definitions..."); - let all_intrinsics = parse_header(&header_content); - println!(" Found {} intrinsic definitions", all_intrinsics.len()); - - // Filter out intrinsics requiring architecture versions not yet supported by rustc - let intrinsics: Vec<_> = all_intrinsics - .into_iter() - .filter(|i| i.min_arch <= MAX_SUPPORTED_ARCH) - .collect(); - let filtered_count = intrinsics.len(); - println!( - " Filtered to {} intrinsics (max supported: hvxv{})", - filtered_count, MAX_SUPPORTED_ARCH - ); - - // Count simple vs compound - let simple_count = intrinsics.iter().filter(|i| !i.is_compound).count(); - let compound_count = intrinsics.iter().filter(|i| i.is_compound).count(); - println!(" Simple intrinsics: {}", simple_count); - println!(" Compound intrinsics: {}", compound_count); - - // Print some sample intrinsics for verification - println!("\n Sample simple intrinsics:"); - for info in intrinsics.iter().filter(|i| !i.is_compound).take(5) { - println!( - " {} -> {} ({})", - info.q6_name, info.builtin_name, info.asm_syntax - ); - } - - println!("\n Sample compound intrinsics:"); - for info in intrinsics.iter().filter(|i| i.is_compound).take(5) { - println!(" {} ({})", info.q6_name, info.asm_syntax); - } - - // Count architecture versions - let mut arch_counts: HashMap = HashMap::new(); - for info in &intrinsics { - *arch_counts.entry(info.min_arch).or_insert(0) += 1; - } - println!("\n By architecture version:"); - let mut archs: Vec<_> = arch_counts.iter().collect(); - archs.sort_by_key(|(k, _)| *k); - for (arch, count) in archs { - println!(" HVX v{}: {} intrinsics", arch, count); - } - - // Generate output files let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); - // Either "check" to check the output versus the committed output, or "bless" // to update the output. let mode = Mode::from_env(); - println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); + run_generator(&hexagon_dir, mode, |out_dir| -> Result<(), String> { - for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { - let path = out_dir.join(filename); - generate_module_file(&intrinsics, &path, vmode)?; - println!(" Output: {}", hexagon_dir.join(filename).display()); - } + // Here scalar::generate writes scalar.rs . + scalar::generate(&crate_dir, out_dir)?; + // Here hvx::generate writes v64.rs and v128.rs . + hvx::generate(&crate_dir, out_dir)?; Ok(()) }) .map_err(|e| e.to_string())?; - println!("\n=== Results ==="); - println!( - " Generated {} simple wrapper functions per module", - simple_count - ); - println!( - " Generated {} compound wrapper functions per module", - compound_count - ); - println!( - " Total: {} functions per module", - simple_count + compound_count - ); - println!(" Output files: v64.rs, v128.rs"); - Ok(()) } diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs similarity index 96% rename from library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs rename to library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs index 934c7e28fe374..4beee35df97ae 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs @@ -21,6 +21,7 @@ use std::collections::HashMap; use std::fs::File; use std::io::Write; use std::path::Path; +use stdarch_gen_common::GENERATED_MARKER; /// Extract the instruction mnemonic from the assembly syntax string. /// @@ -630,6 +631,7 @@ fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> R let mut output = File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_module_doc()).map_err(|e| e.to_string())?; writeln!(output, "").map_err(|e| e.to_string())?; writeln!(output, "{}", generate_extern_block(intrinsics)).map_err(|e| e.to_string())?; @@ -651,28 +653,12 @@ fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> R Ok(()) } -fn main() -> Result<(), String> { - println!("=== Hexagon Scalar Code Generator ===\n"); - - let crate_dir = std::env::var("CARGO_MANIFEST_DIR") - .map(std::path::PathBuf::from) - .unwrap_or_else(|_| std::env::current_dir().unwrap()); - - let header_content = read_header(&crate_dir)?; - println!("Read {} bytes", header_content.len()); - +/// Parse the scalar header in `crate_dir` and write `scalar.rs` into `out_dir`. +pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Result<(), String> { + let header_content = read_header(crate_dir)?; let intrinsics = parse_header(&header_content); - println!("Parsed {} scalar intrinsics", intrinsics.len()); - - let hexagon_dir = std::env::args() - .nth(1) - .map(std::path::PathBuf::from) - .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); - std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; - let scalar_path = hexagon_dir.join("scalar.rs"); - + std::fs::create_dir_all(out_dir).map_err(|e| e.to_string())?; + let scalar_path = out_dir.join("scalar.rs"); generate_scalar_file(&intrinsics, &scalar_path)?; - println!("Generated scalar.rs at {}", scalar_path.display()); - Ok(()) } diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec index 41432adf2508c..d3efb6c9606a2 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec @@ -1872,11 +1872,13 @@ asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrecip_s +impl = portable name = lasx_xvfrecip_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrecip_d +impl = portable name = lasx_xvfrecip_d asm-fmts = xd, xj data-types = V4DF, V4DF @@ -1912,11 +1914,13 @@ asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrsqrt_s +impl = portable name = lasx_xvfrsqrt_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrsqrt_d +impl = portable name = lasx_xvfrsqrt_d asm-fmts = xd, xj data-types = V4DF, V4DF @@ -2067,41 +2071,49 @@ asm-fmts = xd, xj data-types = V4DI, V4DI /// lasx_xvmuh_b +impl = portable name = lasx_xvmuh_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvmuh_h +impl = portable name = lasx_xvmuh_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvmuh_w +impl = portable name = lasx_xvmuh_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvmuh_d +impl = portable name = lasx_xvmuh_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI /// lasx_xvmuh_bu +impl = portable name = lasx_xvmuh_bu asm-fmts = xd, xj, xk data-types = UV32QI, UV32QI, UV32QI /// lasx_xvmuh_hu +impl = portable name = lasx_xvmuh_hu asm-fmts = xd, xj, xk data-types = UV16HI, UV16HI, UV16HI /// lasx_xvmuh_wu +impl = portable name = lasx_xvmuh_wu asm-fmts = xd, xj, xk data-types = UV8SI, UV8SI, UV8SI /// lasx_xvmuh_du +impl = portable name = lasx_xvmuh_du asm-fmts = xd, xj, xk data-types = UV4DI, UV4DI, UV4DI @@ -2560,41 +2572,49 @@ asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrintrz_s +impl = portable name = lasx_xvfrintrz_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrintrz_d +impl = portable name = lasx_xvfrintrz_d asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrintrp_s +impl = portable name = lasx_xvfrintrp_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrintrp_d +impl = portable name = lasx_xvfrintrp_d asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrintrm_s +impl = portable name = lasx_xvfrintrm_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrintrm_d +impl = portable name = lasx_xvfrintrm_d asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvld +impl = portable name = lasx_xvld asm-fmts = xd, rj, si12 data-types = V32QI, CVPOINTER, SI /// lasx_xvst +impl = portable name = lasx_xvst asm-fmts = xd, rj, si12 data-types = VOID, V32QI, CVPOINTER, SI @@ -2681,11 +2701,13 @@ asm-fmts = xd, i13 data-types = V4DI, HI /// lasx_xvldx +impl = portable name = lasx_xvldx asm-fmts = xd, rj, rk data-types = V32QI, CVPOINTER, DI /// lasx_xvstx +impl = portable name = lasx_xvstx asm-fmts = xd, rj, rk data-types = VOID, V32QI, CVPOINTER, DI @@ -3297,21 +3319,25 @@ asm-fmts = xd, xj, xk data-types = V16HI, V16HI, UV32QI, V32QI /// lasx_xvrotr_b +impl = portable name = lasx_xvrotr_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvrotr_h +impl = portable name = lasx_xvrotr_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvrotr_w +impl = portable name = lasx_xvrotr_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvrotr_d +impl = portable name = lasx_xvrotr_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI @@ -3397,21 +3423,25 @@ asm-fmts = xd, xj data-types = UV4DI, UV4DI /// lasx_xvrotri_b +impl = portable name = lasx_xvrotri_b asm-fmts = xd, xj, ui3 data-types = V32QI, V32QI, UQI /// lasx_xvrotri_h +impl = portable name = lasx_xvrotri_h asm-fmts = xd, xj, ui4 data-types = V16HI, V16HI, UQI /// lasx_xvrotri_w +impl = portable name = lasx_xvrotri_w asm-fmts = xd, xj, ui5 data-types = V8SI, V8SI, UQI /// lasx_xvrotri_d +impl = portable name = lasx_xvrotri_d asm-fmts = xd, xj, ui6 data-types = V4DI, V4DI, UQI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec index 211c3c0fcfe62..79dc4217da293 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec @@ -1959,11 +1959,13 @@ asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrecip_s +impl = portable name = lsx_vfrecip_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrecip_d +impl = portable name = lsx_vfrecip_d asm-fmts = vd, vj data-types = V2DF, V2DF @@ -1999,11 +2001,13 @@ asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrsqrt_s +impl = portable name = lsx_vfrsqrt_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrsqrt_d +impl = portable name = lsx_vfrsqrt_d asm-fmts = vd, vj data-types = V2DF, V2DF @@ -2129,41 +2133,49 @@ asm-fmts = vd, vj data-types = V2DI, V2DI /// lsx_vmuh_b +impl = portable name = lsx_vmuh_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vmuh_h +impl = portable name = lsx_vmuh_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vmuh_w +impl = portable name = lsx_vmuh_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vmuh_d +impl = portable name = lsx_vmuh_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI /// lsx_vmuh_bu +impl = portable name = lsx_vmuh_bu asm-fmts = vd, vj, vk data-types = UV16QI, UV16QI, UV16QI /// lsx_vmuh_hu +impl = portable name = lsx_vmuh_hu asm-fmts = vd, vj, vk data-types = UV8HI, UV8HI, UV8HI /// lsx_vmuh_wu +impl = portable name = lsx_vmuh_wu asm-fmts = vd, vj, vk data-types = UV4SI, UV4SI, UV4SI /// lsx_vmuh_du +impl = portable name = lsx_vmuh_du asm-fmts = vd, vj, vk data-types = UV2DI, UV2DI, UV2DI @@ -2623,31 +2635,37 @@ asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrintrz_s +impl = portable name = lsx_vfrintrz_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrintrz_d +impl = portable name = lsx_vfrintrz_d asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrintrp_s +impl = portable name = lsx_vfrintrp_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrintrp_d +impl = portable name = lsx_vfrintrp_d asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrintrm_s +impl = portable name = lsx_vfrintrm_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrintrm_d +impl = portable name = lsx_vfrintrm_d asm-fmts = vd, vj data-types = V2DF, V2DF @@ -3133,21 +3151,25 @@ asm-fmts = vd, vj, vk data-types = V2DI, V2DI, UV2DI, V2DI /// lsx_vrotr_b +impl = portable name = lsx_vrotr_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vrotr_h +impl = portable name = lsx_vrotr_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vrotr_w +impl = portable name = lsx_vrotr_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vrotr_d +impl = portable name = lsx_vrotr_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI @@ -3233,21 +3255,25 @@ asm-fmts = vd, vj data-types = UV2DI, UV2DI /// lsx_vrotri_b +impl = portable name = lsx_vrotri_b asm-fmts = vd, vj, ui3 data-types = V16QI, V16QI, UQI /// lsx_vrotri_h +impl = portable name = lsx_vrotri_h asm-fmts = vd, vj, ui4 data-types = V8HI, V8HI, UQI /// lsx_vrotri_w +impl = portable name = lsx_vrotri_w asm-fmts = vd, vj, ui5 data-types = V4SI, V4SI, UQI /// lsx_vrotri_d +impl = portable name = lsx_vrotri_d asm-fmts = vd, vj, ui6 data-types = V2DI, V2DI, UQI @@ -3503,11 +3529,13 @@ asm-fmts = vd, vj, ui8 data-types = V4SI, V4SI, V4SI, USI /// lsx_vld +impl = portable name = lsx_vld asm-fmts = vd, rj, si12 data-types = V16QI, CVPOINTER, SI /// lsx_vst +impl = portable name = lsx_vst asm-fmts = vd, rj, si12 data-types = VOID, V16QI, CVPOINTER, SI @@ -3559,11 +3587,13 @@ asm-fmts = vd, vj, vk, va data-types = V16QI, V16QI, V16QI, V16QI /// lsx_vldx +impl = portable name = lsx_vldx asm-fmts = vd, rj, rk data-types = V16QI, CVPOINTER, DI /// lsx_vstx +impl = portable name = lsx_vstx asm-fmts = vd, rj, rk data-types = VOID, V16QI, CVPOINTER, DI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt index abbfcb3365d2b..0bd33c248bd3b 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt +++ b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt @@ -263,6 +263,36 @@ lsx_vshuf4i_b lsx_vshuf4i_h lsx_vshuf4i_w lsx_vshuf4i_d +lsx_vld +lsx_vst +lsx_vldx +lsx_vstx +lsx_vrotr_b +lsx_vrotr_h +lsx_vrotr_w +lsx_vrotr_d +lsx_vrotri_b +lsx_vrotri_h +lsx_vrotri_w +lsx_vrotri_d +lsx_vfrecip_s +lsx_vfrecip_d +lsx_vfrsqrt_s +lsx_vfrsqrt_d +lsx_vfrintrp_s +lsx_vfrintrp_d +lsx_vfrintrm_s +lsx_vfrintrm_d +lsx_vfrintrz_s +lsx_vfrintrz_d +lsx_vmuh_b +lsx_vmuh_h +lsx_vmuh_w +lsx_vmuh_d +lsx_vmuh_bu +lsx_vmuh_hu +lsx_vmuh_wu +lsx_vmuh_du # LASX intrinsics lasx_xvsll_b @@ -527,3 +557,33 @@ lasx_xvpackod_d lasx_xvshuf4i_b lasx_xvshuf4i_h lasx_xvshuf4i_w +lasx_xvld +lasx_xvst +lasx_xvldx +lasx_xvstx +lasx_xvrotr_b +lasx_xvrotr_h +lasx_xvrotr_w +lasx_xvrotr_d +lasx_xvrotri_b +lasx_xvrotri_h +lasx_xvrotri_w +lasx_xvrotri_d +lasx_xvfrecip_s +lasx_xvfrecip_d +lasx_xvfrsqrt_s +lasx_xvfrsqrt_d +lasx_xvfrintrp_s +lasx_xvfrintrp_d +lasx_xvfrintrm_s +lasx_xvfrintrm_d +lasx_xvfrintrz_s +lasx_xvfrintrz_d +lasx_xvmuh_b +lasx_xvmuh_h +lasx_xvmuh_w +lasx_xvmuh_d +lasx_xvmuh_bu +lasx_xvmuh_hu +lasx_xvmuh_wu +lasx_xvmuh_du diff --git a/library/stdarch/crates/stdarch-test/Cargo.toml b/library/stdarch/crates/stdarch-test/Cargo.toml index e88258bfd30d0..eddd3b7f78936 100644 --- a/library/stdarch/crates/stdarch-test/Cargo.toml +++ b/library/stdarch/crates/stdarch-test/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" assert-instr-macro = { path = "../assert-instr-macro" } simd-test-macro = { path = "../simd-test-macro" } rustc-demangle = "0.1.8" -cfg-if = "1.0" [target.'cfg(windows)'.dependencies] cc = "1.0" diff --git a/library/stdarch/crates/stdarch-test/src/lib.rs b/library/stdarch/crates/stdarch-test/src/lib.rs index c468ebd12bb0d..9ef25a7957c1d 100644 --- a/library/stdarch/crates/stdarch-test/src/lib.rs +++ b/library/stdarch/crates/stdarch-test/src/lib.rs @@ -6,18 +6,16 @@ #![deny(rust_2018_idioms)] #![allow(clippy::missing_docs_in_private_items, clippy::print_stdout)] -#[macro_use] -extern crate cfg_if; - pub use assert_instr_macro::*; pub use simd_test_macro::*; use std::{cmp, collections::HashSet, env, hash, hint::black_box, str, sync::LazyLock}; -cfg_if! { - if #[cfg(target_arch = "wasm32")] { +cfg_select! { + target_arch = "wasm32" => { pub mod wasm; use wasm::disassemble_myself; - } else { + } + _ => { mod disassembly; use crate::disassembly::disassemble_myself; } diff --git a/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs b/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs index 32726d1bf450b..be948df541b79 100644 --- a/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs +++ b/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs @@ -305,7 +305,7 @@ fn verify_all_signatures() { } // FIXME: these have not been added to Intrinsics Guide yet - if ["amx-avx512", "amx-fp8", "amx-movrs", "amx-tf32", "movrs"] + if ["amx-avx512", "amx-fp8", "amx-movrs", "movrs"] .iter() .any(|f| feature.contains(f)) { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index cb4a3593c51d6..02201f43c08ce 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -56,7 +56,14 @@ cfg_select! { } } -#[cfg(target_env = "musl")] +// `target_abi = "pauthtest"` is currently only used by the Linux/musl +// `aarch64-unknown-linux-pauthtest` target. That target only supports unwinding +// via libunwind. +#[cfg(target_abi = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + +#[cfg(all(target_env = "musl", not(target_abi = "pauthtest")))] cfg_select! { all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); diff --git a/package.json b/package.json index a4f6e18bcf422..6bd8a5be6a105 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "browser-ui-test": "^0.24.0", + "browser-ui-test": "^0.24.1", "es-check": "^9.4.4", "eslint": "^8.57.1", "typescript": "^5.8.3" diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 7bdcb483eb923..0ac18df2084e3 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -140,6 +140,18 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { target: builder.config.host_target, }); + let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); + let results_dir = rustc_perf_dir.join("results"); + builder.create_dir(&results_dir); + + let mut cmd = command(collector.tool_path); + + // We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory + // with compile-time benchmarks. + cmd.current_dir(builder.src.join("src/tools/rustc-perf")); + + let db_path = results_dir.join("results.db"); + let is_profiling = match &args.cmd { PerfCommand::Eprintln { .. } | PerfCommand::Samply { .. } @@ -151,32 +163,23 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); } - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); - builder.std(compiler, builder.config.host_target); - - if let Some(opts) = args.cmd.shared_opts() - && opts.profiles.contains(&Profile::Doc) - { - builder.ensure(Rustdoc { target_compiler: compiler }); - } + let prepare_rustc = || { + let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + builder.std(compiler, builder.config.host_target); - let sysroot = builder.ensure(Sysroot::new(compiler)); - let mut rustc = sysroot.clone(); - rustc.push("bin"); - rustc.push("rustc"); - rustc.set_extension(EXE_EXTENSION); - - let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); - let results_dir = rustc_perf_dir.join("results"); - builder.create_dir(&results_dir); - - let mut cmd = command(collector.tool_path); - - // We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory - // with compile-time benchmarks. - cmd.current_dir(builder.src.join("src/tools/rustc-perf")); + if let Some(opts) = args.cmd.shared_opts() + && opts.profiles.contains(&Profile::Doc) + { + builder.ensure(Rustdoc { target_compiler: compiler }); + } - let db_path = results_dir.join("results.db"); + let sysroot = builder.ensure(Sysroot::new(compiler)); + let mut rustc = sysroot.clone(); + rustc.push("bin"); + rustc.push("rustc"); + rustc.set_extension(EXE_EXTENSION); + rustc + }; match &args.cmd { PerfCommand::Eprintln { opts } @@ -191,7 +194,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); }); cmd.arg("--out-dir").arg(&results_dir); - cmd.arg(rustc); + cmd.arg(prepare_rustc()); apply_shared_opts(&mut cmd, opts); cmd.run(builder); @@ -202,7 +205,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); cmd.arg("bench_local"); cmd.arg("--db").arg(&db_path); cmd.arg("--id").arg(id); - cmd.arg(rustc); + cmd.arg(prepare_rustc()); apply_shared_opts(&mut cmd, opts); cmd.run(builder); diff --git a/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile b/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile index 8f7c7ebe35559..d8bfd6f52cb8b 100644 --- a/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile +++ b/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile @@ -24,13 +24,20 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +ENV PARALLEL_FRONTEND_THREADS=4 +ENV ITERATION_COUNT=2 + ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-sanitizers \ --enable-profiler \ --enable-compiler-docs \ - --set llvm.libzstd=true + --set llvm.libzstd=true \ + --set rust.parallel-frontend-threads=${PARALLEL_FRONTEND_THREADS} # Build the toolchain with multiple parallel frontend threads and then run tests # Tests are still compiled serially at the moment (intended to be changed in follow-ups). -ENV SCRIPT python3 ../x.py --stage 2 test --set rust.parallel-frontend-threads=4 +# Running compiletest only tests for parallel frontend, then the rest without compiletest-only +# options (i.e. `--parallel-frontend-threads=4 --iteration-count=2`) +COPY ./scripts/optional-x86_64-gnu-parallel-frontend.sh /scripts/ +ENV SCRIPT="Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/scripts/optional-x86_64-gnu-parallel-frontend.sh b/src/ci/docker/scripts/optional-x86_64-gnu-parallel-frontend.sh new file mode 100755 index 0000000000000..ed9aed263f25e --- /dev/null +++ b/src/ci/docker/scripts/optional-x86_64-gnu-parallel-frontend.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -eux -o pipefail + +if [ ! -v RUST_TEST_THREADS ]; then + RUST_TEST_THREADS_=$(python3 -c "print(max(1, $(nproc) // ${PARALLEL_FRONTEND_THREADS}))") +else + RUST_TEST_THREADS_=${RUST_TEST_THREADS} +fi + +RUST_TEST_THREADS=${RUST_TEST_THREADS_} \ + python3 ../x.py --stage 2 test \ + tests/ui \ + -- \ + --parallel-frontend-threads="${PARALLEL_FRONTEND_THREADS}" \ + --iteration-count="${ITERATION_COUNT}" diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh index 6ba0c738713b2..c8d67f0e80c4f 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh @@ -19,3 +19,7 @@ set -ex # Rebuild the stdlib with the size optimizations enabled and run tests again. RUSTFLAGS_NOT_BOOTSTRAP="--cfg feature=\"optimize_for_size\"" ../x.py --stage 1 test \ library/std library/alloc library/core + +# Rebuild the stdlib forcing the use of intrinsic fallbacks and run core tests again. +RUSTFLAGS_NOT_BOOTSTRAP="-Zforce-intrinsic-fallback" ../x.py --stage 1 test \ + library/core diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 74f1a0abdde8e..a8eebe7599fda 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -380,6 +380,8 @@ auto: - name: optional-x86_64-gnu-parallel-frontend # This test can be flaky, so do not cancel CI if it fails, for now. continue_on_error: true + env: + DOCKER_SCRIPT: optional-x86_64-gnu-parallel-frontend.sh <<: *job-linux-4c # This job ensures commits landing on nightly still pass the full diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 3cd34f24c6e3d..a8c27c5615007 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -29,33 +29,18 @@ use crate::passes::{self, Condition}; use crate::scrape_examples::{AllCallLocations, ScrapeExamplesOptions}; use crate::{html, opts, theme}; -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub(crate) enum OutputFormat { - Json, - #[default] + /// `--output-format=json` without `--show-coverage`. + /// + /// JSON description of crate API. + IrJson, + /// `--output-format=json` with `--show-coverage`. + CoverageJson, Html, Doctest, } -impl OutputFormat { - pub(crate) fn is_json(&self) -> bool { - matches!(self, OutputFormat::Json) - } -} - -impl TryFrom<&str> for OutputFormat { - type Error = String; - - fn try_from(value: &str) -> Result { - match value { - "json" => Ok(OutputFormat::Json), - "html" => Ok(OutputFormat::Html), - "doctest" => Ok(OutputFormat::Doctest), - _ => Err(format!("unknown output format `{value}`")), - } - } -} - /// Either an input crate, markdown file, or nothing (--merge=finalize). pub(crate) enum InputMode { /// The `--merge=finalize` step does not need an input crate to rustdoc. @@ -329,7 +314,8 @@ pub(crate) enum EmitType { HtmlStaticFiles, HtmlNonStaticFiles, // not explicitly nameable by the user for now - JsonFiles, + IrJsonFiles, + CoverageJsonFiles, DepInfo(Option), } @@ -338,7 +324,8 @@ impl fmt::Display for EmitType { f.write_str(match self { Self::HtmlStaticFiles => "html-static-files", Self::HtmlNonStaticFiles => "html-non-static-files", - Self::JsonFiles => "json-files", + Self::IrJsonFiles => "ir-json-files", + Self::CoverageJsonFiles => "coverage-json-files", Self::DepInfo(_) => "dep-info", }) } @@ -479,40 +466,48 @@ impl Options { let show_coverage = matches.opt_present("show-coverage"); let output_format_s = matches.opt_str("output-format"); - let output_format = match output_format_s { - Some(ref s) => match OutputFormat::try_from(s.as_str()) { - Ok(out_fmt) => out_fmt, - Err(e) => dcx.fatal(e), - }, - None => OutputFormat::default(), + let output_format = match output_format_s.as_deref() { + None | Some("html") => OutputFormat::Html, + Some("json") => { + if show_coverage { + OutputFormat::CoverageJson + } else { + OutputFormat::IrJson + } + } + Some("doctest") => OutputFormat::Doctest, + Some(other) => dcx.fatal(format!("unknown output format `{other}`")), }; - // check for `--output-format=json` + // check for `--output-format` stability, and compatibility with `--show-coverage` match ( output_format_s.as_ref().map(|_| output_format), show_coverage, nightly_options::is_unstable_enabled(matches), ) { - (None | Some(OutputFormat::Json), true, _) => {} + (None | Some(OutputFormat::CoverageJson), true, _) => {} (_, true, _) => { dcx.fatal(format!( "`--output-format={}` is not supported for the `--show-coverage` option", - output_format_s.unwrap_or_default(), + output_format_s.expect("checked for none above"), )); } // If `-Zunstable-options` is used, nothing to check after this point. (_, false, true) => {} (None | Some(OutputFormat::Html), false, _) => {} - (Some(OutputFormat::Json), false, false) => { + (Some(OutputFormat::IrJson), false, false) => { dcx.fatal( - "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", + "the -Z unstable-options flag must be passed to enable --output-format=json for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", ); } (Some(OutputFormat::Doctest), false, false) => { dcx.fatal( - "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/134529)", + "the -Z unstable-options flag must be passed to enable --output-format=doctest (see https://github.com/rust-lang/rust/issues/134529)", ); } + (Some(OutputFormat::CoverageJson), false, _) => { + unreachable!("CoverageJson is only possible when show_coverage is true") + } } let mut emit = FxIndexMap::default(); @@ -531,18 +526,18 @@ impl Options { match typ { EmitType::DepInfo(_) => match output_format { - OutputFormat::Json | OutputFormat::Html => {} + OutputFormat::Html | OutputFormat::IrJson | OutputFormat::CoverageJson => {} OutputFormat::Doctest => unreachable!(), }, EmitType::HtmlStaticFiles | EmitType::HtmlNonStaticFiles => match output_format { OutputFormat::Html => {} - OutputFormat::Json => dcx.fatal(format!( + OutputFormat::IrJson | OutputFormat::CoverageJson => dcx.fatal(format!( "the `--emit={typ}` flag is not supported with `--output-format=json`", )), OutputFormat::Doctest => unreachable!(), }, - EmitType::JsonFiles => unreachable!(), + EmitType::IrJsonFiles | EmitType::CoverageJsonFiles => unreachable!(), } // De-duplicate emit types and the last wins. @@ -558,7 +553,8 @@ impl Options { // will have already been rejected above. if emit.is_empty() { match output_format { - OutputFormat::Json => emit.push(EmitType::JsonFiles), + OutputFormat::IrJson => emit.push(EmitType::IrJsonFiles), + OutputFormat::CoverageJson => emit.push(EmitType::CoverageJsonFiles), OutputFormat::Html => { emit.push(EmitType::HtmlStaticFiles); emit.push(EmitType::HtmlNonStaticFiles); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index fccfa3f57e930..5bdedf7ae3d54 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -75,8 +75,6 @@ pub(crate) struct DocContext<'tcx> { pub(crate) inlined: FxHashSet, /// Used by `calculate_doc_coverage`. pub(crate) output_format: OutputFormat, - /// Used by `strip_private`. - pub(crate) show_coverage: bool, } impl<'tcx> DocContext<'tcx> { @@ -139,7 +137,7 @@ impl<'tcx> DocContext<'tcx> { /// /// If another option like `--show-coverage` is enabled, it will return `false`. pub(crate) fn is_json_output(&self) -> bool { - self.output_format.is_json() && !self.show_coverage + self.output_format == OutputFormat::IrJson } /// If `--document-private-items` was passed to rustdoc. @@ -385,7 +383,6 @@ pub(crate) fn run_global_ctxt( cache: Cache::new(render_options.document_private, render_options.document_hidden), inlined: FxHashSet::default(), output_format, - show_coverage, }; for cnum in tcx.crates(()) { diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 433e4487eb9d5..2d6865dc689b6 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -134,7 +134,7 @@ impl<'tcx> JsonRenderer<'tcx> { impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { const DESCR: &'static str = "json"; const RUN_ON_MODULE: bool = false; - const NON_STATIC_FILE_EMIT_TYPE: EmitType = EmitType::JsonFiles; + const NON_STATIC_FILE_EMIT_TYPE: EmitType = EmitType::IrJsonFiles; type ModuleData = (); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a4f8e3e12e510..17f5af024ac7f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -984,11 +984,13 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { }, ) }), - config::OutputFormat::Json => sess.time("render_json", || { + config::OutputFormat::IrJson => sess.time("render_json", || { run_renderer(krate, render_opts, cache, tcx, json::JsonRenderer::init) }), - // Already handled above with doctest runners. - config::OutputFormat::Doctest => unreachable!(), + // Already handled above with doctest runners or coverage early return + config::OutputFormat::Doctest | config::OutputFormat::CoverageJson => { + unreachable!() + } } }) }) diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 200a5a71b453a..adf98afc46cc0 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -11,6 +11,7 @@ use serde::Serialize; use tracing::debug; use crate::clean; +use crate::config::OutputFormat; use crate::core::DocContext; use crate::html::markdown::{ErrorCodes, find_testable_code}; use crate::passes::Pass; @@ -131,8 +132,7 @@ impl CoverageCalculator<'_, '_> { fn print_results(&self) { let output_format = self.ctx.output_format; - // In this case we want to ensure that the `OutputFormat` is JSON and NOT the `DocContext`. - if output_format.is_json() { + if output_format == OutputFormat::CoverageJson { println!("{}", self.to_json()); return; } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 5cd64d6a4a995..e4541eb2d6061 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -525,7 +525,7 @@ fn is_uninit_value_valid_for_layout<'tcx>(cx: &LateContext<'tcx>, layout: TyAndL match layout.layout.backend_repr { BackendRepr::Scalar(s) => s.is_uninit_valid(), - BackendRepr::ScalarPair(a, b) => a.is_uninit_valid() && b.is_uninit_valid(), + BackendRepr::ScalarPair { a, b, b_offset: _ } => a.is_uninit_valid() && b.is_uninit_valid(), BackendRepr::SimdVector { element, count } => count == 0 || element.is_uninit_valid(), BackendRepr::SimdScalableVector { element, .. } => element.is_uninit_valid(), // Here validity is determined by the structural fields instead. diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 5014541faeb01..9cca7c30817f9 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -313,7 +313,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Immediate::ScalarPair(sc_first, sc_second) => { // The first scalar has an offset of zero; compute the offset of the 2nd. let ofs_second = { - let rustc_abi::BackendRepr::ScalarPair(a, b) = imm.layout.backend_repr + let rustc_abi::BackendRepr::ScalarPair { a: _, b: _, b_offset } = + imm.layout.backend_repr else { span_bug!( this.cur_span(), @@ -321,7 +322,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { imm.layout ) }; - a.size(this).align_to(b.default_align(this).abi).bytes_usize() + b_offset.bytes_usize() }; write_scalar(this, sc_first, 0)?; diff --git a/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr b/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr index 1e9d859b5cf81..1853ecbf43c70 100644 --- a/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr +++ b/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr @@ -1,13 +1,13 @@ error: Undefined Behavior: deallocating ALLOC, which is Rust heap memory, using PLATFORM heap deallocation operation --> RUSTLIB/std/src/sys/alloc/PLATFORM.rs:LL:CC | -LL | FREE(); +LL | FREE(); | ^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: stack backtrace: - 0: std::sys::alloc::PLATFORM::::dealloc + 0: std::sys::alloc::PLATFORM::dealloc at RUSTLIB/std/src/sys/alloc/PLATFORM.rs:LL:CC 1: ::deallocate at RUSTLIB/std/src/alloc.rs:LL:CC diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index ef6c6f0f586ae..a07c2029f4b38 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.25" +wasm-component-ld = "0.5.26" diff --git a/tests/codegen-llvm/range-len-try-from.rs b/tests/codegen-llvm/range-len-try-from.rs new file mode 100644 index 0000000000000..e8f204b45e81b --- /dev/null +++ b/tests/codegen-llvm/range-len-try-from.rs @@ -0,0 +1,43 @@ +// `0..slice.len()` should propagate the same upper bound as +// `slice.iter().enumerate()`, allowing the checked index conversion to be +// removed inside the loop. + +//@ compile-flags: -Copt-level=3 +//@ only-64bit + +#![crate_type = "lib"] + +use std::convert::TryFrom; +use std::hint::black_box; + +// CHECK-LABEL: @score_round_enumerate +#[no_mangle] +pub fn score_round_enumerate(candidates: &[bool]) { + // The length check itself can still fail. + // CHECK: call void {{.*}}unwrap_failed + // But the checked conversion inside the loop should not add another panic path. + // CHECK-NOT: call void {{.*}}unwrap_failed + // CHECK: ret void + u32::try_from(candidates.len()).unwrap(); + + for (i, _) in candidates.iter().enumerate() { + u32::try_from(i).unwrap(); + black_box(42); + } +} + +// CHECK-LABEL: @score_round_range +#[no_mangle] +pub fn score_round_range(candidates: &[bool]) { + // The length check itself can still fail. + // CHECK: call void {{.*}}unwrap_failed + // But the checked conversion inside the loop should not add another panic path. + // CHECK-NOT: call void {{.*}}unwrap_failed + // CHECK: ret void + u32::try_from(candidates.len()).unwrap(); + + for i in 0..candidates.len() { + u32::try_from(i).unwrap(); + black_box(42); + } +} diff --git a/tests/run-make/unstable-flag-required/README.md b/tests/run-make/unstable-flag-required/README.md deleted file mode 100644 index e5251fdf9b5d9..0000000000000 --- a/tests/run-make/unstable-flag-required/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This is a collection of tests that verify `--unstable-options` is required. -It should eventually be removed in favor of UI tests once compiletest stops -passing --unstable-options by default (#82639). diff --git a/tests/run-make/unstable-flag-required/output-format-json.stderr b/tests/run-make/unstable-flag-required/output-format-json.stderr deleted file mode 100644 index fb4079beb2799..0000000000000 --- a/tests/run-make/unstable-flag-required/output-format-json.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error: the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578) - diff --git a/tests/run-make/unstable-flag-required/rmake.rs b/tests/run-make/unstable-flag-required/rmake.rs deleted file mode 100644 index c521436c203d5..0000000000000 --- a/tests/run-make/unstable-flag-required/rmake.rs +++ /dev/null @@ -1,12 +0,0 @@ -// The flag `--output-format` is unauthorized on beta and stable releases, which led -// to confusion for maintainers doing testing on nightly. Tying it to an unstable flag -// elucidates this, and this test checks that `--output-format` cannot be passed on its -// own. -// See https://github.com/rust-lang/rust/pull/82497 - -use run_make_support::{diff, rustdoc}; - -fn main() { - let out = rustdoc().output_format("json").input("x.html").run_fail().stderr_utf8(); - diff().expected_file("output-format-json.stderr").actual_text("actual-json", out).run(); -} diff --git a/tests/run-make/unstable-flag-required/x.rs b/tests/run-make/unstable-flag-required/x.rs deleted file mode 100644 index 5df7576133a68..0000000000000 --- a/tests/run-make/unstable-flag-required/x.rs +++ /dev/null @@ -1 +0,0 @@ -// nothing to see here diff --git a/tests/run-make/wasm-emscripten-main-symbol/main.rs b/tests/run-make/wasm-emscripten-main-symbol/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/wasm-emscripten-main-symbol/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/wasm-emscripten-main-symbol/rmake.rs b/tests/run-make/wasm-emscripten-main-symbol/rmake.rs new file mode 100644 index 0000000000000..03bbdd0bcb62b --- /dev/null +++ b/tests/run-make/wasm-emscripten-main-symbol/rmake.rs @@ -0,0 +1,45 @@ +//@ only-wasm32-unknown-emscripten + +// On wasm the age-old C trick of a `main` that is either `int main(void)` or +// `int main(int, char**)` doesn't work, because wasm requires caller and callee +// signatures to match. The platform ABI (as used by Clang) therefore mangles +// the entry point by signature; the argc/argv form is emitted as +// `__main_argc_argv`. Rust's emscripten target uses the argc/argv form, so its +// entry point must be emitted under that name rather than as a raw `main`. +// +// This matters beyond cosmetics: emscripten's own crt references +// `__main_argc_argv` directly on some entry paths (notably `crt1_proxy_main.o`, +// used by `-sPROXY_TO_PTHREAD`), which fail to link against a raw `main`. +// +// The JS-visible name of the entry point stays `_main`; emscripten bridges that +// to the wasm `__main_argc_argv` export internally. + +use run_make_support::{rfs, rustc, wasmparser}; +use wasmparser::ExternalKind; + +fn main() { + rustc().input("main.rs").target("wasm32-unknown-emscripten").output("main.js").run(); + + let file = rfs::read("main.wasm"); + let mut entry = None; + let mut has_plain_main = false; + for payload in wasmparser::Parser::new(0).parse_all(&file) { + if let wasmparser::Payload::ExportSection(s) = payload.unwrap() { + for export in s { + let export = export.unwrap(); + match export.name { + "__main_argc_argv" => entry = Some(export.kind), + "main" => has_plain_main = true, + _ => {} + } + } + } + } + + assert_eq!( + entry, + Some(ExternalKind::Func), + "the emscripten entry point must be exported as the wasm C-ABI symbol `__main_argc_argv`", + ); + assert!(!has_plain_main, "a raw `main` symbol must not be exported on wasm"); +} diff --git a/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.rs b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.rs new file mode 100644 index 0000000000000..0609b515e5ca8 --- /dev/null +++ b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --show-coverage --output-format=json --emit=dep-info -Zunstable-options +//@ build-pass diff --git a/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.stdout b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.stdout new file mode 100644 index 0000000000000..f3ba8205ddedf --- /dev/null +++ b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.stdout @@ -0,0 +1 @@ +{"$DIR/output-format-coveragejson-emit-depinfo.rs":{"total":1,"with_docs":0,"total_examples":0,"with_examples":0}} diff --git a/tests/rustdoc-ui/output-format-doctests-unstable.rs b/tests/rustdoc-ui/output-format-doctests-unstable.rs new file mode 100644 index 0000000000000..b409f619fcc42 --- /dev/null +++ b/tests/rustdoc-ui/output-format-doctests-unstable.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --output-format=doctest +pub struct Foo; + +//~? ERROR the -Z unstable-options flag must be passed to enable --output-format=doctest (see https://github.com/rust-lang/rust/issues/134529) diff --git a/tests/rustdoc-ui/output-format-doctests-unstable.stderr b/tests/rustdoc-ui/output-format-doctests-unstable.stderr new file mode 100644 index 0000000000000..b4e920efb2b0f --- /dev/null +++ b/tests/rustdoc-ui/output-format-doctests-unstable.stderr @@ -0,0 +1,2 @@ +error: the -Z unstable-options flag must be passed to enable --output-format=doctest (see https://github.com/rust-lang/rust/issues/134529) + diff --git a/tests/rustdoc-ui/output-format-irjson-emit-depinfo.rs b/tests/rustdoc-ui/output-format-irjson-emit-depinfo.rs new file mode 100644 index 0000000000000..7b5e7eaf998d4 --- /dev/null +++ b/tests/rustdoc-ui/output-format-irjson-emit-depinfo.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --output-format=json --emit=dep-info -Zunstable-options +//@ build-pass diff --git a/tests/rustdoc-ui/output-format-irjson-unstable.rs b/tests/rustdoc-ui/output-format-irjson-unstable.rs new file mode 100644 index 0000000000000..88ba753616bb9 --- /dev/null +++ b/tests/rustdoc-ui/output-format-irjson-unstable.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --output-format=json +pub struct Foo; + +//~? ERROR the -Z unstable-options flag must be passed to enable --output-format=json for documentation diff --git a/tests/rustdoc-ui/output-format-irjson-unstable.stderr b/tests/rustdoc-ui/output-format-irjson-unstable.stderr new file mode 100644 index 0000000000000..dd9cea9301189 --- /dev/null +++ b/tests/rustdoc-ui/output-format-irjson-unstable.stderr @@ -0,0 +1,2 @@ +error: the -Z unstable-options flag must be passed to enable --output-format=json for documentation generation (see https://github.com/rust-lang/rust/issues/76578) + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.html_non_static_coverage.stderr b/tests/rustdoc-ui/output-format-json-emit-html.html_non_static_coverage.stderr new file mode 100644 index 0000000000000..8d8e8c6d12281 --- /dev/null +++ b/tests/rustdoc-ui/output-format-json-emit-html.html_non_static_coverage.stderr @@ -0,0 +1,2 @@ +error: the `--emit=html-non-static-files` flag is not supported with `--output-format=json` + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.html_static_coverage.stderr b/tests/rustdoc-ui/output-format-json-emit-html.html_static_coverage.stderr new file mode 100644 index 0000000000000..bc1ddc1b155d1 --- /dev/null +++ b/tests/rustdoc-ui/output-format-json-emit-html.html_static_coverage.stderr @@ -0,0 +1,2 @@ +error: the `--emit=html-static-files` flag is not supported with `--output-format=json` + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.rs b/tests/rustdoc-ui/output-format-json-emit-html.rs index 7ce3bf756ec92..f97392bab5b68 100644 --- a/tests/rustdoc-ui/output-format-json-emit-html.rs +++ b/tests/rustdoc-ui/output-format-json-emit-html.rs @@ -1,7 +1,11 @@ -//@ revisions: html_static html_non_static +//@ revisions: html_static html_non_static html_static_coverage html_non_static_coverage //@ compile-flags: -Zunstable-options --output-format json //@[html_static] compile-flags: --emit html-static-files +//@[html_static_coverage] compile-flags: --emit html-static-files --show-coverage //@[html_non_static] compile-flags: --emit html-non-static-files +//@[html_non_static_coverage] compile-flags: --emit html-non-static-files --show-coverage //[html_static]~? ERROR the `--emit=html-static-files` flag is not supported with `--output-format=json` +//[html_static_coverage]~? ERROR the `--emit=html-static-files` flag is not supported with `--output-format=json` //[html_non_static]~? ERROR the `--emit=html-non-static-files` flag is not supported with `--output-format=json` +//[html_non_static_coverage]~? ERROR the `--emit=html-non-static-files` flag is not supported with `--output-format=json` diff --git a/tests/rustdoc-ui/output-format-unknown.rs b/tests/rustdoc-ui/output-format-unknown.rs new file mode 100644 index 0000000000000..281a9ceb4cfd7 --- /dev/null +++ b/tests/rustdoc-ui/output-format-unknown.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --output-format=da-doo-ron-ron + +pub struct Foo; +//~? ERROR unknown output format `da-doo-ron-ron` diff --git a/tests/rustdoc-ui/output-format-unknown.stderr b/tests/rustdoc-ui/output-format-unknown.stderr new file mode 100644 index 0000000000000..0f34e62b628ef --- /dev/null +++ b/tests/rustdoc-ui/output-format-unknown.stderr @@ -0,0 +1,2 @@ +error: unknown output format `da-doo-ron-ron` + diff --git a/tests/ui/abi/too-big-byval-abi-issue-148853.rs b/tests/ui/abi/too-big-byval-abi-issue-148853.rs new file mode 100644 index 0000000000000..503ef06ff98f3 --- /dev/null +++ b/tests/ui/abi/too-big-byval-abi-issue-148853.rs @@ -0,0 +1,12 @@ +//@ only-64bit +//@ edition: 2024 +//@ build-fail + +#![crate_type = "lib"] + +#[repr(C)] +pub struct Big([u8; 1 << 63]); + +#[unsafe(no_mangle)] +pub extern "C" fn foo(_x: Big) {} +//~^ ERROR values of the type `[u8; 9223372036854775808]` are too big for the target architecture diff --git a/tests/ui/abi/too-big-byval-abi-issue-148853.stderr b/tests/ui/abi/too-big-byval-abi-issue-148853.stderr new file mode 100644 index 0000000000000..b9247040ded25 --- /dev/null +++ b/tests/ui/abi/too-big-byval-abi-issue-148853.stderr @@ -0,0 +1,8 @@ +error: values of the type `[u8; 9223372036854775808]` are too big for the target architecture + --> $DIR/too-big-byval-abi-issue-148853.rs:11:1 + | +LL | pub extern "C" fn foo(_x: Big) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/allocator/not-an-allocator.u.stderr b/tests/ui/allocator/not-an-allocator.u.stderr index f7400d16b1fd1..9be5dd495db02 100644 --- a/tests/ui/allocator/not-an-allocator.u.stderr +++ b/tests/ui/allocator/not-an-allocator.u.stderr @@ -4,10 +4,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:5:11 @@ -15,10 +16,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied @@ -27,10 +29,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied @@ -39,10 +42,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index f08d1200b9fbc..74d2bf16190de 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -102,22 +102,23 @@ error: layout_of(S) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -174,22 +175,23 @@ error: layout_of(Result) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -222,22 +224,23 @@ error: layout_of(Result) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -249,22 +252,23 @@ error: layout_of(Result) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/layout/enum-scalar-pair-int-ptr.rs b/tests/ui/layout/enum-scalar-pair-int-ptr.rs index 184f61fe79653..ff16c7e7c3af8 100644 --- a/tests/ui/layout/enum-scalar-pair-int-ptr.rs +++ b/tests/ui/layout/enum-scalar-pair-int-ptr.rs @@ -1,6 +1,7 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" //@ normalize-stderr: "Int\(I[0-9]+," -> "Int(I?," //@ normalize-stderr: "valid_range: 0..=[0-9]+" -> "valid_range: $$VALID_RANGE" +//@ normalize-stderr: "b_offset: Size\([0-9]+ bytes\)" -> "b_offset: Size(? bytes)" //! Enum layout tests related to scalar pairs with an int/ptr common primitive. diff --git a/tests/ui/layout/enum-scalar-pair-int-ptr.stderr b/tests/ui/layout/enum-scalar-pair-int-ptr.stderr index 5d54fd432371e..afba5a24ee23d 100644 --- a/tests/ui/layout/enum-scalar-pair-int-ptr.stderr +++ b/tests/ui/layout/enum-scalar-pair-int-ptr.stderr @@ -1,11 +1,11 @@ -error: backend_repr: ScalarPair(Initialized { value: Int(I?, false), valid_range: $VALID_RANGE }, Initialized { value: Pointer(AddressSpace(0)), valid_range: $VALID_RANGE }) - --> $DIR/enum-scalar-pair-int-ptr.rs:12:1 +error: backend_repr: ScalarPair { a: Initialized { value: Int(I?, false), valid_range: $VALID_RANGE }, b: Initialized { value: Pointer(AddressSpace(0)), valid_range: $VALID_RANGE }, b_offset: Size(? bytes) } + --> $DIR/enum-scalar-pair-int-ptr.rs:13:1 | LL | enum ScalarPairPointerWithInt { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: backend_repr: Memory { sized: true } - --> $DIR/enum-scalar-pair-int-ptr.rs:21:1 + --> $DIR/enum-scalar-pair-int-ptr.rs:22:1 | LL | enum NotScalarPairPointerWithSmallerInt { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/layout/enum.stderr b/tests/ui/layout/enum.stderr index 3b41129456dda..e9732d9373a48 100644 --- a/tests/ui/layout/enum.stderr +++ b/tests/ui/layout/enum.stderr @@ -10,7 +10,7 @@ error: size: Size(16 bytes) LL | enum UninhabitedVariantSpace { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=1 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }) +error: backend_repr: ScalarPair { a: Initialized { value: Int(I8, false), valid_range: 0..=1 }, b: Initialized { value: Int(I8, false), valid_range: 0..=255 }, b_offset: Size(1 bytes) } --> $DIR/enum.rs:21:1 | LL | enum ScalarPairDifferingSign { diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index 7e6294f894c3e..021d7b36ac5de 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -3,21 +3,22 @@ error: layout_of(MissingPayloadField) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -50,21 +51,22 @@ error: layout_of(MissingPayloadField) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -100,22 +102,23 @@ error: layout_of(CommonPayloadField) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -148,22 +151,23 @@ error: layout_of(CommonPayloadField) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -175,22 +179,23 @@ error: layout_of(CommonPayloadField) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -216,21 +221,22 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -263,21 +269,22 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -289,21 +296,22 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -329,21 +337,22 @@ error: layout_of(NicheFirst) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=4, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -380,22 +389,23 @@ error: layout_of(NicheFirst) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=2, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(0 bytes), Size(1 bytes), @@ -452,21 +462,22 @@ error: layout_of(NicheSecond) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=4, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -503,22 +514,23 @@ error: layout_of(NicheSecond) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=2, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), Size(0 bytes), diff --git a/tests/ui/macros/local-ambiguity-option-ordering.rs b/tests/ui/macros/local-ambiguity-option-ordering.rs new file mode 100644 index 0000000000000..fd9d8e17ef48f --- /dev/null +++ b/tests/ui/macros/local-ambiguity-option-ordering.rs @@ -0,0 +1,49 @@ +// Test the order in which meta-variable options causing ambiguity are presented in error messages. + +#![crate_type = "lib"] + +macro_rules! ambiguity_1 { + ($($i:ident)* $j:ident) => {}; +} + +ambiguity_1!(error); //~ ERROR local ambiguity + +macro_rules! ambiguity_2 { + ( $( $( ; $i:ident )? $( $j:ident )? ; )* ) => {}; +} + +ambiguity_2!( ; error ); //~ ERROR local ambiguity +// +// Parse tree: +// - `$(...)*`: +// - Try one repetition +// - `$(; $i)?`: +// - Try entering +// - Parse `;` +// ----> `$i` can parse `error` +// - Try ignoring +// - `$($j)?`: +// - Try entering +// - `$j` cannot parse `;` +// - failure +// - Try ignoring +// - Parse `;` +// - `$(...)*`: +// - Try another repetition +// - `$(; $i)?`: +// - Try entering +// - Cannot parse `;` +// - failure +// - Try ignoring +// - `$($j)?`: +// - Try entering +// ------------> `$j` can parse `error` +// - Try ignoring +// - Cannot parse `;` +// - failure +// - Try ignoring +// - EOF vs `error` +// - failure +// - Try no repetitions +// - EOF vs `;` +// - failure diff --git a/tests/ui/macros/local-ambiguity-option-ordering.stderr b/tests/ui/macros/local-ambiguity-option-ordering.stderr new file mode 100644 index 0000000000000..a6c79595767a0 --- /dev/null +++ b/tests/ui/macros/local-ambiguity-option-ordering.stderr @@ -0,0 +1,14 @@ +error: local ambiguity when calling macro `ambiguity_1`: multiple parsing options: built-in NTs ident ('i') or ident ('j'). + --> $DIR/local-ambiguity-option-ordering.rs:9:14 + | +LL | ambiguity_1!(error); + | ^^^^^ + +error: local ambiguity when calling macro `ambiguity_2`: multiple parsing options: built-in NTs ident ('i') or ident ('j'). + --> $DIR/local-ambiguity-option-ordering.rs:15:17 + | +LL | ambiguity_2!( ; error ); + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr index 28fafa7800305..3b94d3751f227 100644 --- a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr index 45193552b507f..757bbba9c381a 100644 --- a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr +++ b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], diff --git a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr index 28fafa7800305..3b94d3751f227 100644 --- a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr +++ b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr index 28fafa7800305..3b94d3751f227 100644 --- a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/repr/repr-c-int-dead-variants.stderr b/tests/ui/repr/repr-c-int-dead-variants.stderr index c2f7fec38c81f..13051fbdaf493 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.stderr +++ b/tests/ui/repr/repr-c-int-dead-variants.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariantsU8) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariantsU8) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariantsU8) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], diff --git a/tests/ui/suggestions/suggest-add-self-issue-131084.rs b/tests/ui/suggestions/suggest-add-self-issue-131084.rs new file mode 100644 index 0000000000000..adad0f56857d8 --- /dev/null +++ b/tests/ui/suggestions/suggest-add-self-issue-131084.rs @@ -0,0 +1,28 @@ +//@ check-fail + +// A recovered `¶m` should not make the missing-`self` suggestion insert +// the receiver after the leading `&`. + +struct SomeStruct; + +impl SomeStruct { + fn some_fn(&some_name) { + //~^ ERROR expected one of `:`, `@`, or `|`, found `)` + self + //~^ ERROR expected value, found module `self` + } + + fn mut_param(mut some_name) { + //~^ ERROR expected one of `:`, `@`, or `|`, found `)` + self + //~^ ERROR expected value, found module `self` + } + + fn type_before_name(String s) { + //~^ ERROR expected one of `:`, `@`, or `|`, found `s` + self + //~^ ERROR expected value, found module `self` + } +} + +fn main() {} diff --git a/tests/ui/suggestions/suggest-add-self-issue-131084.stderr b/tests/ui/suggestions/suggest-add-self-issue-131084.stderr new file mode 100644 index 0000000000000..1af7eec726273 --- /dev/null +++ b/tests/ui/suggestions/suggest-add-self-issue-131084.stderr @@ -0,0 +1,93 @@ +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/suggest-add-self-issue-131084.rs:9:26 + | +LL | fn some_fn(&some_name) { + | ^ expected one of `:`, `@`, or `|` + | +help: if this is a `self` type, give it a parameter name + | +LL | fn some_fn(self: &some_name) { + | +++++ +help: if this is a parameter name, give it a type + | +LL - fn some_fn(&some_name) { +LL + fn some_fn(some_name: &TypeName) { + | +help: if this is a type, explicitly ignore the parameter name + | +LL | fn some_fn(_: &some_name) { + | ++ + +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/suggest-add-self-issue-131084.rs:15:31 + | +LL | fn mut_param(mut some_name) { + | ^ expected one of `:`, `@`, or `|` + | +help: if this is a `self` type, give it a parameter name + | +LL | fn mut_param(self: mut some_name) { + | +++++ +help: if this is a parameter name, give it a type + | +LL | fn mut_param(mut some_name: TypeName) { + | ++++++++++ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn mut_param(_: mut some_name) { + | ++ + +error: expected one of `:`, `@`, or `|`, found `s` + --> $DIR/suggest-add-self-issue-131084.rs:21:32 + | +LL | fn type_before_name(String s) { + | -------^ + | | | + | | expected one of `:`, `@`, or `|` + | help: declare the type after the parameter binding: `: ` + +error[E0424]: expected value, found module `self` + --> $DIR/suggest-add-self-issue-131084.rs:11:9 + | +LL | fn some_fn(&some_name) { + | ------- this function doesn't have a `self` parameter +LL | +LL | self + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn some_fn(&self, &some_name) { + | ++++++ + +error[E0424]: expected value, found module `self` + --> $DIR/suggest-add-self-issue-131084.rs:17:9 + | +LL | fn mut_param(mut some_name) { + | --------- this function doesn't have a `self` parameter +LL | +LL | self + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn mut_param(&self, mut some_name) { + | ++++++ + +error[E0424]: expected value, found module `self` + --> $DIR/suggest-add-self-issue-131084.rs:23:9 + | +LL | fn type_before_name(String s) { + | ---------------- this function doesn't have a `self` parameter +LL | +LL | self + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn type_before_name(&self, String s) { + | ++++++ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0424`. diff --git a/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr b/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr index ca26e3dd03f35..f656a274703ee 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr @@ -12,7 +12,7 @@ help: the trait `PartialEq` is not implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/self-referential-3.stderr b/tests/ui/type-alias-impl-trait/self-referential-3.stderr index 8592de243adac..7bc4c6def624d 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-3.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-3.stderr @@ -13,7 +13,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/self-referential-4.stderr b/tests/ui/type-alias-impl-trait/self-referential-4.stderr index c6bf1973fcbb1..76c5611906d34 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-4.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-4.stderr @@ -12,7 +12,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `Foo<'static, 'b>` --> $DIR/self-referential-4.rs:13:31 @@ -28,7 +28,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `Moo<'static, 'a>` --> $DIR/self-referential-4.rs:20:31 @@ -44,7 +44,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/type-alias-impl-trait/self-referential.stderr b/tests/ui/type-alias-impl-trait/self-referential.stderr index 7b4e6e9cac528..62d85466cde1a 100644 --- a/tests/ui/type-alias-impl-trait/self-referential.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential.stderr @@ -13,7 +13,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` --> $DIR/self-referential.rs:14:31 @@ -30,7 +30,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` --> $DIR/self-referential.rs:22:31 @@ -47,7 +47,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/type/pattern_types/non_null.stderr b/tests/ui/type/pattern_types/non_null.stderr index 7d3e61770c2c7..88ff73ca2a4a4 100644 --- a/tests/ui/type/pattern_types/non_null.stderr +++ b/tests/ui/type/pattern_types/non_null.stderr @@ -143,8 +143,8 @@ error: layout_of((*const [u8]) is !null) = Layout { align: AbiAlign { abi: Align(8 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Pointer( AddressSpace( 0, @@ -152,14 +152,15 @@ error: layout_of((*const [u8]) is !null) = Layout { ), valid_range: 1..=18446744073709551615, }, - Initialized { + b: Initialized { value: Int( I64, false, ), valid_range: 0..=18446744073709551615, }, - ), + b_offset: Size(8 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), diff --git a/yarn.lock b/yarn.lock index cd35ba9089e46..3d5855dec80cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -74,18 +74,18 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@puppeteer/browsers@3.0.4": - version "3.0.4" - resolved "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.0.4.tgz" - integrity sha512-HGM8iAmGTf+Y7t0373szVbTmt3d7vPkYL/1bpOkOFO0YUYLgSeuYBCzESklogNPvOBnZ/MRD5f07OkpqH1trtA== +"@puppeteer/browsers@3.0.6": + version "3.0.6" + resolved "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.0.6.tgz" + integrity sha512-B/gKoqlFkzhvzsI6jo9K1cZz9o5ypviVv/xu8CwA4grZzyVwN+XfkT+tu8T1zrauuEXv6VhS2oGX+6NL95WcKA== dependencies: modern-tar "^0.7.6" - yargs "^17.7.2" + yargs "^18.0.0" "@ungap/structured-clone@^1.2.0": - version "1.3.1" - resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz" - integrity sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ== + version "1.3.0" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== acorn-jsx@^5.3.2: version "5.3.2" @@ -112,13 +112,23 @@ ansi-regex@^5.0.1: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^4.0.0, ansi-styles@^4.1.0: +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + +ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" +ansi-styles@^6.2.1: + version "6.2.3" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + argparse@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" @@ -130,9 +140,9 @@ balanced-match@^1.0.0: integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== brace-expansion@^1.1.7: - version "1.1.15" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz" - integrity sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg== + version "1.1.14" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz" + integrity sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -144,10 +154,10 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -browser-ui-test@^0.24.0: - version "0.24.0" - resolved "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.24.0.tgz" - integrity sha512-jVpAdq/M1cCHG/2K6pAS8NUYJ6BcNSHune72JOheeoASk+oLuGcWcY1SalYAdkWet3dlyfUSyKKtq+DDjYgdVg== +browser-ui-test@^0.24.1: + version "0.24.1" + resolved "https://registry.yarnpkg.com/browser-ui-test/-/browser-ui-test-0.24.1.tgz#1b186b652e2c6593ae1d698408d0e6ba0f32bff5" + integrity sha512-+S+gDOxc7GGfDUmZeRUcCAUzBVbD6qS7TzZSg1CFN/lgJyXf2MCG8CUbvHTsEi/oG5Iuuza1XzKDPpAAvfSUNw== dependencies: css-unit-converter "^1.1.2" pngjs "^3.4.0" @@ -175,14 +185,14 @@ chromium-bidi@16.0.1: mitt "^3.0.1" zod "^3.24.1" -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== +cliui@^9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz" + integrity sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w== dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" + string-width "^7.2.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" color-convert@^2.0.1: version "2.0.1" @@ -227,10 +237,10 @@ deep-is@^0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -devtools-protocol@0.0.1624250: - version "0.0.1624250" - resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1624250.tgz" - integrity sha512-YFAat/lOiIk0ARmBweG+ygrEcbZrq5B9urRyUoeQKp53MlidHXE2TmTbxKcaXoQj7u/aX+jebDO4BW55rs0WwA== +devtools-protocol@0.0.1638949: + version "0.0.1638949" + resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1638949.tgz" + integrity sha512-mXwg4Fqnv0WR4iuAT/gYUmctNkjILwXFHyZ+m7Ty1dfr0ezZt2U3gnrrJTfRobJTHoXf+IbuFvFITzLrLFjwJA== doctrine@^3.0.0: version "3.0.0" @@ -239,10 +249,10 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^10.3.0: + version "10.6.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz" + integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== es-check@^9.4.4: version "9.6.4" @@ -431,6 +441,11 @@ get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-east-asian-width@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz" + integrity sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA== + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" @@ -510,11 +525,6 @@ is-extglob@^2.1.1: resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" @@ -538,9 +548,9 @@ isexe@^2.0.0: integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== js-yaml@^4.1.0: - version "4.2.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz" - integrity sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw== + version "4.1.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" @@ -706,28 +716,28 @@ punycode@^2.1.0: resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -puppeteer-core@25.1.0: - version "25.1.0" - resolved "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.1.0.tgz" - integrity sha512-jKzy5y4WG6uNuFbTWgW1D7mqoT9o0nllc/6a1DGF775T1mPmgw3scdFEtEq67yVFikavQmbYq6NLfbTfxHSlqQ== +puppeteer-core@25.3.0: + version "25.3.0" + resolved "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.3.0.tgz" + integrity sha512-fm+wpUr2oigH1PXZvwgATrM2tYWHMDG8ASzTEe9uukCye4X5Ldx1K5BTHPFKITrIWvQQAQ256d1NpbEveBcKjA== dependencies: - "@puppeteer/browsers" "3.0.4" + "@puppeteer/browsers" "3.0.6" chromium-bidi "16.0.1" - devtools-protocol "0.0.1624250" + devtools-protocol "0.0.1638949" typed-query-selector "^2.12.2" webdriver-bidi-protocol "0.4.2" ws "^8.21.0" puppeteer@^25.1.0: - version "25.1.0" - resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-25.1.0.tgz" - integrity sha512-7L6/0JM7XStK99lIL4xQySyNEXNfII6pk0BxkI5kKBTOhR7AsoQiv067YTsE/rIXxQiq9ajlO4WcqBjS/FWK1A== + version "25.3.0" + resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-25.3.0.tgz" + integrity sha512-O1tx8S315aw8eI99HZ5ZNcVEzJ9+jKF//eO5UvfZ3cXJ6okZ5sX3Y50u7DJaM+ewEK4LqXP068tBhfRaWikj+g== dependencies: - "@puppeteer/browsers" "3.0.4" + "@puppeteer/browsers" "3.0.6" chromium-bidi "16.0.1" - devtools-protocol "0.0.1624250" + devtools-protocol "0.0.1638949" lilconfig "^3.1.3" - puppeteer-core "25.1.0" + puppeteer-core "25.3.0" typed-query-selector "^2.12.2" queue-microtask@^1.2.2: @@ -740,11 +750,6 @@ readline-sync@^1.4.10: resolved "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz" integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -781,22 +786,29 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== +string-width@^7.0.0, string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + dependencies: + ansi-regex "^6.2.2" + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" @@ -867,14 +879,14 @@ word-wrap@^1.2.5: resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^9.0.0: + version "9.0.2" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz" + integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" wrappy@1: version "1.0.2" @@ -891,23 +903,22 @@ y18n@^5.0.5: resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs-parser@^22.0.0: + version "22.0.0" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz" + integrity sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw== -yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== +yargs@^18.0.0: + version "18.0.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz" + integrity sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg== dependencies: - cliui "^8.0.1" + cliui "^9.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" + string-width "^7.2.0" y18n "^5.0.5" - yargs-parser "^21.1.1" + yargs-parser "^22.0.0" yocto-queue@^0.1.0: version "0.1.0"