From cd39f5a5fe0dededd4d53823fe395f98f0747cc2 Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 14 Dec 2025 14:35:07 +0800 Subject: [PATCH 01/13] Add waker_fn and local_waker_fn to std::task Signed-off-by: tison --- library/alloc/src/task.rs | 76 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index b4116f4988b64..73b7322419744 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -127,6 +127,44 @@ impl From> for RawWaker { } } +/// Converts a closure into a [`Waker`]. +/// +/// The closure gets called every time the waker is woken. +/// +/// # Examples +/// +/// ``` +/// #![feature(waker_fn)] +/// use std::task::waker_fn; +/// +/// let waker = waker_fn(|| println!("woken")); +/// +/// waker.wake_by_ref(); // Prints "woken". +/// waker.wake(); // Prints "woken". +/// ``` +#[cfg(target_has_atomic = "ptr")] +#[unstable(feature = "waker_fn", issue = "149580")] +pub fn waker_fn(f: F) -> Waker { + struct WakeFn { + f: F, + } + + impl Wake for WakeFn + where + F: Fn(), + { + fn wake(self: Arc) { + (self.f)() + } + + fn wake_by_ref(self: &Arc) { + (self.f)() + } + } + + Waker::from(Arc::new(WakeFn { f })) +} + // NB: This private function for constructing a RawWaker is used, rather than // inlining this into the `From> for RawWaker` impl, to ensure that // the safety of `From> for Waker` does not depend on the correct @@ -306,6 +344,44 @@ impl From> for RawWaker { } } +/// Converts a closure into a [`LocalWaker`]. +/// +/// The closure gets called every time the local waker is woken. +/// +/// # Examples +/// +/// ``` +/// #![feature(local_waker)] +/// #![feature(waker_fn)] +/// use std::task::local_waker_fn; +/// +/// let waker = local_waker_fn(|| println!("woken")); +/// +/// waker.wake_by_ref(); // Prints "woken". +/// waker.wake(); // Prints "woken". +/// ``` +#[unstable(feature = "waker_fn", issue = "149580")] +pub fn local_waker_fn(f: F) -> LocalWaker { + struct LocalWakeFn { + f: F, + } + + impl LocalWake for LocalWakeFn + where + F: Fn(), + { + fn wake(self: Rc) { + (self.f)() + } + + fn wake_by_ref(self: &Rc) { + (self.f)() + } + } + + LocalWaker::from(Rc::new(LocalWakeFn { f })) +} + // NB: This private function for constructing a RawWaker is used, rather than // inlining this into the `From> for RawWaker` impl, to ensure that // the safety of `From> for Waker` does not depend on the correct From b00739765c89f9182a8284c1718de7c858217869 Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Tue, 16 Dec 2025 13:25:27 -0800 Subject: [PATCH 02/13] Update provider API docs --- library/core/src/error.rs | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 9ca91ee009ee9..011d6ac4a1c78 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -205,6 +205,56 @@ pub trait Error: Debug + Display { /// assert!(request_ref::(dyn_error).is_none()); /// } /// ``` + /// + /// # Delegating Impls + /// + ///
+ /// + /// **Warning**: We recommend implementors avoid delegating implementations of `provide` to + /// source error implementations. + /// + ///
+ /// + /// This method should expose context from the current piece of the source chain only, not from + /// sources that are exposed in the chain of sources. Delegating `provide` implementations cause + /// the same context to be provided by multiple errors in the chain of sources which can cause + /// unintended duplication of information in error reports or require heuristics to deduplicate. + /// + /// In other words, the following implementation pattern for `provide` is discouraged and should + /// not be used for [`Error`] types exposed in public APIs to third parties. + /// + /// ```rust + /// # #![feature(error_generic_member_access)] + /// # use core::fmt; + /// # use core::error::Request; + /// # #[derive(Debug)] + /// struct MyError { + /// source: Error, + /// } + /// # #[derive(Debug)] + /// # struct Error; + /// # impl fmt::Display for Error { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "Example Source Error") + /// # } + /// # } + /// # impl fmt::Display for MyError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "Example Error") + /// # } + /// # } + /// # impl std::error::Error for Error { } + /// + /// impl std::error::Error for MyError { + /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + /// Some(&self.source) + /// } + /// + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// self.source.provide(request) // <--- Discouraged + /// } + /// } + /// ``` #[unstable(feature = "error_generic_member_access", issue = "99301")] #[allow(unused_variables)] fn provide<'a>(&'a self, request: &mut Request<'a>) {} From 5610d84ab12502922459ae5da17ac8e0774a8e44 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 19 Dec 2025 07:55:00 -0800 Subject: [PATCH 03/13] rustc: Fix `-Zexport-executable-symbols` on wasm This commit reorders some cases in `export_symbols` in the linker implementation for wasm to ensure that the `is_like_wasm` case is handled before the catch-all `CrateType::Executable` case. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index c73e950bed408..b47652092ed5c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -839,6 +839,11 @@ impl<'a> Linker for GccLinker<'a> { self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); } self.link_arg(path); + } else if self.sess.target.is_like_wasm { + self.link_arg("--no-export-dynamic"); + for (sym, _) in symbols { + self.link_arg("--export").link_arg(sym); + } } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris { let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; @@ -853,11 +858,6 @@ impl<'a> Linker for GccLinker<'a> { self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); } self.link_arg("--dynamic-list").link_arg(path); - } else if self.sess.target.is_like_wasm { - self.link_arg("--no-export-dynamic"); - for (sym, _) in symbols { - self.link_arg("--export").link_arg(sym); - } } else { // Write an LD version script let res: io::Result<()> = try { From f89cce3acb5106b98f6ada765677dc11e41518a0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 17 Dec 2025 14:33:10 +0100 Subject: [PATCH 04/13] `c_variadic`: provide `va_arg` for more targets --- compiler/rustc_codegen_llvm/src/va_arg.rs | 106 +++++++++++++++++----- 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index b23415a732cc7..688f461e7478a 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,12 +1,13 @@ -use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size, TyAndLayout}; +use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods, }; +use rustc_middle::bug; use rustc_middle::ty::Ty; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_target::spec::{Abi, Arch, Env}; use crate::builder::Builder; @@ -82,6 +83,7 @@ enum PassMode { enum SlotSize { Bytes8 = 8, Bytes4 = 4, + Bytes1 = 1, } enum AllowHigherAlign { @@ -728,7 +730,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, reg_addr: &'ll Value, - layout: TyAndLayout<'tcx, Ty<'tcx>>, + layout: TyAndLayout<'tcx>, src_align: Align, ) -> &'ll Value { if layout.layout.align.abi > src_align { @@ -751,7 +753,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, va_list_addr: &'ll Value, - layout: TyAndLayout<'tcx, Ty<'tcx>>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { let dl = bx.cx.data_layout(); let ptr_align_abi = dl.data_layout().pointer_align().abi; @@ -1003,15 +1005,17 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi); } +/// Determine the va_arg implementation to use. The LLVM va_arg instruction +/// is lacking in some instances, so we should only use it as a fallback. pub(super) fn emit_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, addr: OperandRef<'tcx, &'ll Value>, target_ty: Ty<'tcx>, ) -> &'ll Value { - // Determine the va_arg implementation to use. The LLVM va_arg instruction - // is lacking in some instances, so we should only use it as a fallback. - let target = &bx.cx.tcx.sess.target; + let layout = bx.cx.layout_of(target_ty); + let target_ty_size = layout.layout.size().bytes(); + let target = &bx.cx.tcx.sess.target; match target.arch { Arch::X86 => emit_ptr_va_arg( bx, @@ -1069,23 +1073,79 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( AllowHigherAlign::Yes, ForceRightAdjust::No, ), + Arch::LoongArch32 => emit_ptr_va_arg( + bx, + addr, + target_ty, + if target_ty_size > 2 * 4 { PassMode::Indirect } else { PassMode::Direct }, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::LoongArch64 => emit_ptr_va_arg( + bx, + addr, + target_ty, + if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct }, + SlotSize::Bytes8, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::AmdGpu => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes4, + AllowHigherAlign::No, + ForceRightAdjust::No, + ), + Arch::Nvptx64 => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes1, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::Wasm32 => emit_ptr_va_arg( + bx, + addr, + target_ty, + if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() { + PassMode::Indirect + } else { + PassMode::Direct + }, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::Wasm64 => bug!("c-variadic functions are not fully implemented for wasm64"), + Arch::CSky => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), // Windows x86_64 - Arch::X86_64 if target.is_like_windows => { - let target_ty_size = bx.cx.size_of(target_ty).bytes(); - emit_ptr_va_arg( - bx, - addr, - target_ty, - if target_ty_size > 8 || !target_ty_size.is_power_of_two() { - PassMode::Indirect - } else { - PassMode::Direct - }, - SlotSize::Bytes8, - AllowHigherAlign::No, - ForceRightAdjust::No, - ) - } + Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg( + bx, + addr, + target_ty, + if target_ty_size > 8 || !target_ty_size.is_power_of_two() { + PassMode::Indirect + } else { + PassMode::Direct + }, + SlotSize::Bytes8, + AllowHigherAlign::No, + ForceRightAdjust::No, + ), // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64. Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty), Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty), From 38ab51943c5726ce71f5656e8345e0487f94e2ce Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jan 2026 09:28:05 +0100 Subject: [PATCH 05/13] ./x check miri: enable check_only feature --- src/bootstrap/src/core/build_steps/check.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index df82b7baa9f97..7da2551c76fe9 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -793,8 +793,7 @@ tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustcPrivate tool_check_step!(Miri { path: "src/tools/miri", mode: Mode::ToolRustcPrivate, - enable_features: ["stack-cache"], - default_features: false, + enable_features: ["check_only"], }); tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustcPrivate }); tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustcPrivate }); From dd948f96f30af19cf2cb7041de577ee1bf29bb9f Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 6 Jan 2026 09:42:42 +0800 Subject: [PATCH 06/13] Thread `--jobs` from `bootstrap` -> `compiletest` -> `run-make-support` --- src/bootstrap/src/core/build_steps/test.rs | 2 ++ src/tools/compiletest/src/common.rs | 5 +++++ src/tools/compiletest/src/directives/tests.rs | 1 + src/tools/compiletest/src/lib.rs | 10 +++++++++- src/tools/compiletest/src/runtest/run_make.rs | 3 +++ src/tools/compiletest/src/rustdoc_gui_test.rs | 1 + src/tools/run-make-support/src/env.rs | 14 ++++++++++++++ tests/run-make/compiletest-self-test/jobs/rmake.rs | 5 +++++ 8 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/compiletest-self-test/jobs/rmake.rs diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index a3c13fc4b0959..4008ede6c0d4c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2265,6 +2265,8 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--with-std-remap-debuginfo"); } + cmd.arg("--jobs").arg(builder.jobs().to_string()); + let mut llvm_components_passed = false; let mut copts_passed = false; if builder.config.llvm_enabled(test_compiler.host) { diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5563abe92a80b..800ce4f3088e3 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -715,6 +715,11 @@ pub struct Config { pub override_codegen_backend: Option, /// Whether to ignore `//@ ignore-backends`. pub bypass_ignore_backends: bool, + + /// Number of parallel jobs configured for the build. + /// + /// This is forwarded from bootstrap's `jobs` configuration. + pub jobs: u32, } impl Config { diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 0d3777b8e60c6..71343080cfa20 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -225,6 +225,7 @@ impl ConfigBuilder { "--nightly-branch=", "--git-merge-commit-email=", "--minicore-path=", + "--jobs=0", ]; let mut args: Vec = args.iter().map(ToString::to_string).collect(); diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index a64c7850aad4d..997f570393f25 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -218,7 +218,8 @@ fn parse_config(args: Vec) -> Config { "the codegen backend to use instead of the default one", "CODEGEN BACKEND [NAME | PATH]", ) - .optflag("", "bypass-ignore-backends", "ignore `//@ ignore-backends` directives"); + .optflag("", "bypass-ignore-backends", "ignore `//@ ignore-backends` directives") + .reqopt("", "jobs", "number of parallel jobs bootstrap was configured with", "JOBS"); let (argv0, args_) = args.split_first().unwrap(); if args.len() == 1 || args[1] == "-h" || args[1] == "--help" { @@ -363,6 +364,11 @@ fn parse_config(args: Vec) -> Config { let build_test_suite_root = opt_path(matches, "build-test-suite-root"); assert!(build_test_suite_root.starts_with(&build_root)); + let jobs = match matches.opt_str("jobs") { + Some(jobs) => jobs.parse::().expect("expected `--jobs` to be an `u32`"), + None => panic!("`--jobs` is required"), + }; + Config { bless: matches.opt_present("bless"), fail_fast: matches.opt_present("fail-fast") @@ -481,6 +487,8 @@ fn parse_config(args: Vec) -> Config { default_codegen_backend, override_codegen_backend, bypass_ignore_backends: matches.opt_present("bypass-ignore-backends"), + + jobs, } } diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index ba3a123473671..4eb8f91fe8946 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -249,6 +249,9 @@ impl TestCx<'_> { cmd.env("__STD_REMAP_DEBUGINFO_ENABLED", "1"); } + // Used for `run_make_support::env::jobs`. + cmd.env("__BOOTSTRAP_JOBS", self.config.jobs.to_string()); + // We don't want RUSTFLAGS set from the outside to interfere with // compiler flags set in the test cases: cmd.env_remove("RUSTFLAGS"); diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index 4454ffb1f59e6..06b66ef9fd0a0 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -139,5 +139,6 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { default_codegen_backend: CodegenBackend::Llvm, override_codegen_backend: None, bypass_ignore_backends: Default::default(), + jobs: Default::default(), } } diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs index 507d51471df01..65b6d3db85d5e 100644 --- a/src/tools/run-make-support/src/env.rs +++ b/src/tools/run-make-support/src/env.rs @@ -49,3 +49,17 @@ pub fn set_current_dir>(dir: P) { std::env::set_current_dir(dir.as_ref()) .expect(&format!("could not set current directory to \"{}\"", dir.as_ref().display())); } + +/// Number of parallel jobs bootstrap was configured with. +/// +/// This may fallback to [`std::thread::available_parallelism`] when no explicit jobs count has been +/// configured. Refer to bootstrap's jobs fallback logic. +#[track_caller] +pub fn jobs() -> u32 { + std::env::var_os("__BOOTSTRAP_JOBS") + .expect("`__BOOTSTRAP_JOBS` must be set by `compiletest`") + .to_str() + .expect("`__BOOTSTRAP_JOBS` must be a valid string") + .parse::() + .expect("`__BOOTSTRAP_JOBS` must be a valid `u32`") +} diff --git a/tests/run-make/compiletest-self-test/jobs/rmake.rs b/tests/run-make/compiletest-self-test/jobs/rmake.rs new file mode 100644 index 0000000000000..d21b44ada1c3f --- /dev/null +++ b/tests/run-make/compiletest-self-test/jobs/rmake.rs @@ -0,0 +1,5 @@ +//! Very basic smoke test to make sure `run_make_support::env::jobs` at least does not panic. + +fn main() { + println!("{}", run_make_support::env::jobs()); +} From 630c7596e959e6ab9b18552e81081e5ff346b1c3 Mon Sep 17 00:00:00 2001 From: kulst Date: Thu, 1 Jan 2026 19:25:29 +0100 Subject: [PATCH 07/13] Ensure that static initializers are acyclic for NVPTX NVPTX does not support cycles in static initializers. LLVM produces an error when attempting to codegen such constructs (like self referential structs). To not produce LLVM UB we instead emit a post-monomorphization error on Rust side before reaching codegen. This is achieved by analysing a subgraph of the "mono item graph" that only contains statics: 1. Calculate the strongly connected components (SCCs) of the graph 2. Check for cycles (more than one node in a SCC or exactly one node which references itself) --- Cargo.lock | 1 + compiler/rustc_monomorphize/Cargo.toml | 1 + compiler/rustc_monomorphize/messages.ftl | 4 + compiler/rustc_monomorphize/src/collector.rs | 3 +- compiler/rustc_monomorphize/src/errors.rs | 12 ++ .../src/graph_checks/mod.rs | 18 +++ .../src/graph_checks/statics.rs | 115 ++++++++++++++++++ compiler/rustc_monomorphize/src/lib.rs | 1 + .../rustc_monomorphize/src/partitioning.rs | 3 + compiler/rustc_target/src/spec/json.rs | 3 + compiler/rustc_target/src/spec/mod.rs | 4 + .../src/spec/targets/nvptx64_nvidia_cuda.rs | 3 + .../platform-support/nvptx64-nvidia-cuda.md | 33 +++++ tests/auxiliary/minicore.rs | 2 +- ...static-initializer-acyclic-issue-146787.rs | 29 +++++ ...ic-initializer-acyclic-issue-146787.stderr | 32 +++++ 16 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 compiler/rustc_monomorphize/src/graph_checks/mod.rs create mode 100644 compiler/rustc_monomorphize/src/graph_checks/statics.rs create mode 100644 tests/ui/static/static-initializer-acyclic-issue-146787.rs create mode 100644 tests/ui/static/static-initializer-acyclic-issue-146787.stderr diff --git a/Cargo.lock b/Cargo.lock index 816bb1a37859f..d0d47f882a89c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4421,6 +4421,7 @@ dependencies = [ "rustc_errors", "rustc_fluent_macro", "rustc_hir", + "rustc_index", "rustc_macros", "rustc_middle", "rustc_session", diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 09a55f0b5f8da..0829d52283abf 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -10,6 +10,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } +rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index edb91d8f2eda1..09500ba73359b 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -75,4 +75,8 @@ monomorphize_recursion_limit = monomorphize_start_not_found = using `fn main` requires the standard library .help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]` +monomorphize_static_initializer_cyclic = static initializer forms a cycle involving `{$head}` + .label = part of this cycle + .note = cyclic static initializers are not supported for target `{$target}` + monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4b2f8e03afc13..070db1ae6b5ee 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -267,7 +267,8 @@ pub(crate) struct UsageMap<'tcx> { // Maps every mono item to the mono items used by it. pub used_map: UnordMap, Vec>>, - // Maps every mono item to the mono items that use it. + // Maps each mono item with users to the mono items that use it. + // Be careful: subsets `used_map`, so unused items are vacant. user_map: UnordMap, Vec>>, } diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 4949d9ae3922d..723649f22117e 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -117,3 +117,15 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { /// Whether this is a problem at a call site or at a declaration. pub is_call: bool, } + +#[derive(Diagnostic)] +#[diag(monomorphize_static_initializer_cyclic)] +#[note] +pub(crate) struct StaticInitializerCyclic<'a> { + #[primary_span] + pub span: Span, + #[label] + pub labels: Vec, + pub head: &'a str, + pub target: &'a str, +} diff --git a/compiler/rustc_monomorphize/src/graph_checks/mod.rs b/compiler/rustc_monomorphize/src/graph_checks/mod.rs new file mode 100644 index 0000000000000..2b9b7cfff0b21 --- /dev/null +++ b/compiler/rustc_monomorphize/src/graph_checks/mod.rs @@ -0,0 +1,18 @@ +//! Checks that need to operate on the entire mono item graph +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::TyCtxt; + +use crate::collector::UsageMap; +use crate::graph_checks::statics::check_static_initializers_are_acyclic; + +mod statics; + +pub(super) fn target_specific_checks<'tcx, 'a, 'b>( + tcx: TyCtxt<'tcx>, + mono_items: &'a [MonoItem<'tcx>], + usage_map: &'b UsageMap<'tcx>, +) { + if tcx.sess.target.options.static_initializer_must_be_acyclic { + check_static_initializers_are_acyclic(tcx, mono_items, usage_map); + } +} diff --git a/compiler/rustc_monomorphize/src/graph_checks/statics.rs b/compiler/rustc_monomorphize/src/graph_checks/statics.rs new file mode 100644 index 0000000000000..a764d307b3d4b --- /dev/null +++ b/compiler/rustc_monomorphize/src/graph_checks/statics.rs @@ -0,0 +1,115 @@ +use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::{DirectedGraph, Successors}; +use rustc_data_structures::unord::UnordMap; +use rustc_hir::def_id::DefId; +use rustc_index::{Idx, IndexVec, newtype_index}; +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::TyCtxt; + +use crate::collector::UsageMap; +use crate::errors; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +struct StaticNodeIdx(usize); + +impl Idx for StaticNodeIdx { + fn new(idx: usize) -> Self { + Self(idx) + } + + fn index(self) -> usize { + self.0 + } +} + +impl From for StaticNodeIdx { + fn from(value: usize) -> Self { + StaticNodeIdx(value) + } +} + +newtype_index! { + #[derive(Ord, PartialOrd)] + struct StaticSccIdx {} +} + +// Adjacency-list graph for statics using `StaticNodeIdx` as node type. +// We cannot use `DefId` as the node type directly because each node must be +// represented by an index in the range `0..num_nodes`. +struct StaticRefGraph<'a, 'b, 'tcx> { + // maps from `StaticNodeIdx` to `DefId` and vice versa + statics: &'a FxIndexSet, + // contains for each `MonoItem` the `MonoItem`s it uses + used_map: &'b UnordMap, Vec>>, +} + +impl<'a, 'b, 'tcx> DirectedGraph for StaticRefGraph<'a, 'b, 'tcx> { + type Node = StaticNodeIdx; + + fn num_nodes(&self) -> usize { + self.statics.len() + } +} + +impl<'a, 'b, 'tcx> Successors for StaticRefGraph<'a, 'b, 'tcx> { + fn successors(&self, node_idx: StaticNodeIdx) -> impl Iterator { + let def_id = self.statics[node_idx.index()]; + self.used_map[&MonoItem::Static(def_id)].iter().filter_map(|&mono_item| match mono_item { + MonoItem::Static(def_id) => self.statics.get_index_of(&def_id).map(|idx| idx.into()), + _ => None, + }) + } +} + +pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>( + tcx: TyCtxt<'tcx>, + mono_items: &'a [MonoItem<'tcx>], + usage_map: &'b UsageMap<'tcx>, +) { + // Collect statics + let statics: FxIndexSet = mono_items + .iter() + .filter_map(|&mono_item| match mono_item { + MonoItem::Static(def_id) => Some(def_id), + _ => None, + }) + .collect(); + + // If we don't have any statics the check is not necessary + if statics.is_empty() { + return; + } + // Create a subgraph from the mono item graph, which only contains statics + let graph = StaticRefGraph { statics: &statics, used_map: &usage_map.used_map }; + // Calculate its SCCs + let sccs: Sccs = Sccs::new(&graph); + // Group statics by SCCs + let mut nodes_of_sccs: IndexVec> = + IndexVec::from_elem_n(Vec::new(), sccs.num_sccs()); + for i in graph.iter_nodes() { + nodes_of_sccs[sccs.scc(i)].push(i); + } + let is_cyclic = |nodes_of_scc: &[StaticNodeIdx]| -> bool { + match nodes_of_scc.len() { + 0 => false, + 1 => graph.successors(nodes_of_scc[0]).any(|x| x == nodes_of_scc[0]), + 2.. => true, + } + }; + // Emit errors for all cycles + for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) { + // We sort the nodes by their Span to have consistent error line numbers + nodes.sort_by_key(|node| tcx.def_span(statics[node.index()])); + + let head_def = statics[nodes[0].index()]; + let head_span = tcx.def_span(head_def); + + tcx.dcx().emit_err(errors::StaticInitializerCyclic { + span: head_span, + labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(), + head: &tcx.def_path_str(head_def), + target: &tcx.sess.target.llvm_target, + }); + } +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 8b48cf5a6501d..5b4f74ca6a708 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -16,6 +16,7 @@ use rustc_span::ErrorGuaranteed; mod collector; mod errors; +mod graph_checks; mod mono_checks; mod partitioning; mod util; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 1c8d6db08c316..6a1d64bd28bc1 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -124,6 +124,7 @@ use tracing::debug; use crate::collector::{self, MonoItemCollectionStrategy, UsageMap}; use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined}; +use crate::graph_checks::target_specific_checks; struct PartitioningCx<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -1125,6 +1126,8 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio }; let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy); + // Perform checks that need to operate on the entire mono item graph + target_specific_checks(tcx, &items, &usage_map); // If there was an error during collection (e.g. from one of the constants we evaluated), // then we stop here. This way codegen does not have to worry about failing constants. diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index a972299deeac4..20fbb687b3080 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -163,6 +163,7 @@ impl Target { forward!(relro_level); forward!(archive_format); forward!(allow_asm); + forward!(static_initializer_must_be_acyclic); forward!(main_needs_argc_argv); forward!(has_thread_local); forward!(obj_is_bitcode); @@ -360,6 +361,7 @@ impl ToJson for Target { target_option_val!(relro_level); target_option_val!(archive_format); target_option_val!(allow_asm); + target_option_val!(static_initializer_must_be_acyclic); target_option_val!(main_needs_argc_argv); target_option_val!(has_thread_local); target_option_val!(obj_is_bitcode); @@ -581,6 +583,7 @@ struct TargetSpecJson { relro_level: Option, archive_format: Option>, allow_asm: Option, + static_initializer_must_be_acyclic: Option, main_needs_argc_argv: Option, has_thread_local: Option, obj_is_bitcode: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b06339f594257..89c9fdc935cc5 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2394,6 +2394,9 @@ pub struct TargetOptions { pub archive_format: StaticCow, /// Is asm!() allowed? Defaults to true. pub allow_asm: bool, + /// Static initializers must be acyclic. + /// Defaults to false + pub static_initializer_must_be_acyclic: bool, /// Whether the runtime startup code requires the `main` function be passed /// `argc` and `argv` values. pub main_needs_argc_argv: bool, @@ -2777,6 +2780,7 @@ impl Default for TargetOptions { archive_format: "gnu".into(), main_needs_argc_argv: true, allow_asm: true, + static_initializer_must_be_acyclic: false, has_thread_local: false, obj_is_bitcode: false, min_atomic_width: None, diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index be09681b1f35b..87c2693e9877f 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -59,6 +59,9 @@ pub(crate) fn target() -> Target { // Support using `self-contained` linkers like the llvm-bitcode-linker link_self_contained: LinkSelfContainedDefault::True, + // Static initializers must not have cycles on this target + static_initializer_must_be_acyclic: true, + ..Default::default() }, } diff --git a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md index 0eb7e1d84bd0a..c722a7086967b 100644 --- a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md +++ b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md @@ -49,6 +49,39 @@ $ rustup component add llvm-tools --toolchain nightly $ rustup component add llvm-bitcode-linker --toolchain nightly ``` +## Target specific restrictions + +The PTX instruction set architecture has special requirements regarding what is +and isn't allowed. In order to avoid producing invalid PTX or generating undefined +behavior by LLVM, some Rust language features are disallowed when compiling for this target. + +### Static initializers must be acyclic + +A static's initializer must not form a cycle with itself or another static's +initializer. Therefore, the compiler will reject not only the self-referencing static `A`, +but all of the following statics. + +```Rust +struct Foo(&'static Foo); + +static A: Foo = Foo(&A); //~ ERROR static initializer forms a cycle involving `A` + +static B0: Foo = Foo(&B1); //~ ERROR static initializer forms a cycle involving `B0` +static B1: Foo = Foo(&B0); + +static C0: Foo = Foo(&C1); //~ ERROR static initializer forms a cycle involving `C0` +static C1: Foo = Foo(&C2); +static C2: Foo = Foo(&C0); +``` + +Initializers that are acyclic are allowed: + +```Rust +struct Bar(&'static u32); + +static BAR: Bar = Bar(&INT); // is allowed +static INT: u32 = 42u32; // also allowed +``` $DIR/static-initializer-acyclic-issue-146787.rs:21:1 + | +LL | static C0: Foo = Foo(&C1); + | ^^^^^^^^^^^^^^ part of this cycle +LL | static C1: Foo = Foo(&C2); + | -------------- part of this cycle +LL | static C2: Foo = Foo(&C0); + | -------------- part of this cycle + | + = note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda` + +error: static initializer forms a cycle involving `B0` + --> $DIR/static-initializer-acyclic-issue-146787.rs:18:1 + | +LL | static B0: Foo = Foo(&B1); + | ^^^^^^^^^^^^^^ part of this cycle +LL | static B1: Foo = Foo(&B0); + | -------------- part of this cycle + | + = note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda` + +error: static initializer forms a cycle involving `A` + --> $DIR/static-initializer-acyclic-issue-146787.rs:16:1 + | +LL | static A: Foo = Foo(&A); + | ^^^^^^^^^^^^^ part of this cycle + | + = note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda` + +error: aborting due to 3 previous errors + From af69f157129e154f050a224ba384703e57115032 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Tue, 6 Jan 2026 22:09:11 +0100 Subject: [PATCH 08/13] Add AtomicPtr::null --- library/core/src/sync/atomic.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 1eae06ebd33e7..c675fd1381d85 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1550,6 +1550,23 @@ impl AtomicPtr { unsafe { &*ptr.cast() } } + /// Creates a new `AtomicPtr` initialized with a null pointer. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let atomic_ptr = AtomicPtr::<()>::null(); + /// assert!(atomic_ptr.load(Ordering::Relaxed).is_null()); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "atomic_ptr_null", issue = "150733")] + pub const fn null() -> AtomicPtr { + AtomicPtr::new(crate::ptr::null_mut()) + } + /// Returns a mutable reference to the underlying pointer. /// /// This is safe because the mutable reference guarantees that no other threads are From fff9c623bfc75bce9f9e5c64ed67d9b04ea655c1 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Tue, 6 Jan 2026 23:38:31 +0100 Subject: [PATCH 09/13] Add feature to doc example --- library/core/src/sync/atomic.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index c675fd1381d85..22f46ec385ced 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1555,6 +1555,7 @@ impl AtomicPtr { /// # Examples /// /// ``` + /// #![feature(atomic_ptr_null)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// /// let atomic_ptr = AtomicPtr::<()>::null(); From fe4a9b473273a2d5a9cbf09e8b34ef0b3501ac89 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Wed, 7 Jan 2026 01:23:43 +0100 Subject: [PATCH 10/13] Add amdgpu_dispatch_ptr intrinsic Add a rustc intrinsic `amdgpu_dispatch_ptr` to access the kernel dispatch packet on amdgpu. The HSA kernel dispatch packet contains important information like the launch size and workgroup size. The Rust intrinsic lowers to the `llvm.amdgcn.dispatch.ptr` LLVM intrinsic, which returns a `ptr addrspace(4)`, plus an addrspacecast to `addrspace(0)`, so it can be returned as a Rust reference. The returned pointer/reference is valid for the whole program lifetime, and is therefore `'static`. The return type of the intrinsic (`*const ()`) does not mention the struct so that rustc does not need to know the exact struct type. An alternative would be to define the struct as lang item or add a generic argument to the function. Short version: ```rust #[cfg(target_arch = "amdgpu")] pub fn amdgpu_dispatch_ptr() -> *const (); ``` --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 6 +++++ .../rustc_codegen_ssa/src/mir/intrinsic.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/gpu.rs | 23 ++++++++++++++++ library/core/src/intrinsics/mod.rs | 1 + tests/codegen-llvm/amdgpu-dispatch-ptr.rs | 27 +++++++++++++++++++ 7 files changed, 61 insertions(+) create mode 100644 library/core/src/intrinsics/gpu.rs create mode 100644 tests/codegen-llvm/amdgpu-dispatch-ptr.rs diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b4057eea735ea..8803963f4bd33 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -560,6 +560,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { return Ok(()); } + sym::amdgpu_dispatch_ptr => { + let val = self.call_intrinsic("llvm.amdgcn.dispatch.ptr", &[], &[]); + // Relying on `LLVMBuildPointerCast` to produce an addrspacecast + self.pointercast(val, self.type_ptr()) + } + _ if name.as_str().starts_with("simd_") => { // Unpack non-power-of-2 #[repr(packed, simd)] arguments. // This gives them the expected layout of a regular #[repr(simd)] vector. diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f4fae40d8828f..f5ee9406f4bf1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -112,6 +112,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::unreachable | sym::cold_path | sym::breakpoint + | sym::amdgpu_dispatch_ptr | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid | sym::assert_inhabited diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 4e8333f678b66..d4c4a73e64a29 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -70,6 +70,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::add_with_overflow | sym::aggregate_raw_ptr | sym::align_of + | sym::amdgpu_dispatch_ptr | sym::assert_inhabited | sym::assert_mem_uninitialized_valid | sym::assert_zero_valid @@ -285,6 +286,7 @@ pub(crate) fn check_intrinsic_type( let (n_tps, n_cts, inputs, output) = match intrinsic_name { sym::autodiff => (4, 0, vec![param(0), param(1), param(2)], param(3)), sym::abort => (0, 0, vec![], tcx.types.never), + sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72709753b1dff..676e9a9ae0424 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -454,6 +454,7 @@ symbols! { alu32, always, amdgpu, + amdgpu_dispatch_ptr, analysis, and, and_then, diff --git a/library/core/src/intrinsics/gpu.rs b/library/core/src/intrinsics/gpu.rs new file mode 100644 index 0000000000000..9e7624841d0c6 --- /dev/null +++ b/library/core/src/intrinsics/gpu.rs @@ -0,0 +1,23 @@ +//! Intrinsics for GPU targets. +//! +//! Intrinsics in this module are intended for use on GPU targets. +//! They can be target specific but in general GPU targets are similar. + +#![unstable(feature = "gpu_intrinsics", issue = "none")] + +/// Returns a pointer to the HSA kernel dispatch packet. +/// +/// A `gpu-kernel` on amdgpu is always launched through a kernel dispatch packet. +/// The dispatch packet contains the workgroup size, launch size and other data. +/// The content is defined by the [HSA Platform System Architecture Specification], +/// which is implemented e.g. in AMD's [hsa.h]. +/// The intrinsic returns a unit pointer so that rustc does not need to know the packet struct. +/// The pointer is valid for the whole lifetime of the program. +/// +/// [HSA Platform System Architecture Specification]: https://hsafoundation.com/wp-content/uploads/2021/02/HSA-SysArch-1.2.pdf +/// [hsa.h]: https://github.com/ROCm/rocm-systems/blob/rocm-7.1.0/projects/rocr-runtime/runtime/hsa-runtime/inc/hsa.h#L2959 +#[rustc_nounwind] +#[rustc_intrinsic] +#[cfg(target_arch = "amdgpu")] +#[must_use = "returns a pointer that does nothing unless used"] +pub fn amdgpu_dispatch_ptr() -> *const (); diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index d46d3ed9d5137..0c26aba8618ed 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -60,6 +60,7 @@ use crate::{mem, ptr}; mod bounds; pub mod fallback; +pub mod gpu; pub mod mir; pub mod simd; diff --git a/tests/codegen-llvm/amdgpu-dispatch-ptr.rs b/tests/codegen-llvm/amdgpu-dispatch-ptr.rs new file mode 100644 index 0000000000000..ed8d80e2d3d22 --- /dev/null +++ b/tests/codegen-llvm/amdgpu-dispatch-ptr.rs @@ -0,0 +1,27 @@ +// Tests the amdgpu_dispatch_ptr intrinsic. + +//@ compile-flags: --crate-type=rlib --target amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +//@ needs-llvm-components: amdgpu +//@ add-minicore +#![feature(intrinsics, no_core, rustc_attrs)] +#![no_core] + +extern crate minicore; + +pub struct DispatchPacket { + pub header: u16, + pub setup: u16, + pub workgroup_size_x: u16, // and more +} + +#[rustc_intrinsic] +#[rustc_nounwind] +fn amdgpu_dispatch_ptr() -> *const (); + +// CHECK: %[[ORIG_PTR:[^ ]+]] = tail call ptr addrspace(4) @llvm.amdgcn.dispatch.ptr() +// CHECK: %[[PTR:[^ ]+]] = addrspacecast ptr addrspace(4) %[[ORIG_PTR]] to ptr +// CHECK: ret ptr %[[PTR]] +#[unsafe(no_mangle)] +pub fn get_dispatch_data() -> &'static DispatchPacket { + unsafe { &*(amdgpu_dispatch_ptr() as *const _) } +} From 6346d14202ce2c4ac3e92595a6fd1b619803145d Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 7 Jan 2026 09:31:59 +0800 Subject: [PATCH 11/13] Apply suggestion from @tisonkun --- library/alloc/src/task.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 73b7322419744..aa1901314e37c 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -360,6 +360,7 @@ impl From> for RawWaker { /// waker.wake_by_ref(); // Prints "woken". /// waker.wake(); // Prints "woken". /// ``` +// #[unstable(feature = "local_waker", issue = "118959")] #[unstable(feature = "waker_fn", issue = "149580")] pub fn local_waker_fn(f: F) -> LocalWaker { struct LocalWakeFn { From 600102c09b4e2a65969b1a1c1e857552fe1b83c2 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Thu, 8 Jan 2026 02:12:49 +0100 Subject: [PATCH 12/13] Add myself as co-maintainer for s390x-unknown-linux-musl Having two dedicated target maintainers is a prerequisite for promoting this target to tier 2. I've been in contact with Ulrich and he's agreed to having me as a co-maintainer in preparation for a MCP to promote it to tier 2. --- src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md index b8bee11055fe5..cb7d055154530 100644 --- a/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md +++ b/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md @@ -7,6 +7,7 @@ IBM z/Architecture (s390x) targets (including IBM Z and LinuxONE) running Linux. ## Target maintainers [@uweigand](https://github.com/uweigand) +[@Gelbpunkt](https://github.com/Gelbpunkt) ## Requirements From a3b72d3de584a524d677bbaedf440f9223d439c0 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Thu, 8 Jan 2026 02:06:20 +0000 Subject: [PATCH 13/13] Fix copy-n-paste error in `vtable_for` docs This is a safe function, which doesn't take a `ptr` parameter. --- library/core/src/intrinsics/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 0ae8d3d4a4ce1..27e1673c51de7 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2738,10 +2738,6 @@ pub unsafe fn vtable_align(ptr: *const ()) -> usize; /// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler. /// In some cases, that resolution can exceed the recursion limit, /// and compilation will fail instead of this function returning `None`. -/// -/// # Safety -/// -/// `ptr` must point to a vtable. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic]