From ae174cf351beece10b20a614497e3853058fdb53 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Mon, 7 Sep 2026 11:15:39 -0700 Subject: [PATCH 1/6] Make `std::fs::{File, ReadDir, DirEntry}` always `needs_drop` even when unsupported. This eliminates the portability hazard (and inconsistent linting of `clippy::drop_non_drop`) of types having destructors on some platforms and not others. It is arguably a breaking change, but a breaking change that would also be required if any of these platforms later gains filesystem support. An alternative to this change would be to implement `Drop` on the `std::fs` types instead of the `std::sys` types. That would be more reliable, but would be more of a public API change since `T: Drop` bounds are unfortunately possible. --- library/std/src/sys/fs/unsupported.rs | 19 +++++++++++++++++++ library/std/src/sys/fs/vexos.rs | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 512af27401dfe..b0ad9a9c96dc8 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -29,6 +29,25 @@ pub struct FileType(!); #[derive(Debug)] pub struct DirBuilder {} +// Dummy Drops so that `needs_drop::()` etc. is true independent of platform, +// avoiding a portability hazard. +// The types not listed here are pure data and don’t implement Drop even when real. +impl Drop for File { + fn drop(&mut self) { + self.0 + } +} +impl Drop for ReadDir { + fn drop(&mut self) { + self.0 + } +} +impl Drop for DirEntry { + fn drop(&mut self) { + self.0 + } +} + impl FileAttr { pub fn size(&self) -> u64 { self.0 diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 1357787c44c77..c2417627c0b9d 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -477,6 +477,13 @@ impl Drop for File { } } +impl Drop for ReadDir { + fn drop(&mut self) { + // Dummy Drop so that `needs_drop::()` is true independent of platform. + self.0 + } +} + pub fn readdir(_p: &Path) -> io::Result { // While there *is* a userspace function for reading file directories, // the necessary implementation cannot currently be done cleanly, as From 9da36d0ca22939409424266e55df40706db5fcdc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 5 Oct 2026 17:37:30 +0200 Subject: [PATCH 2/6] Collect all limit attributes during TyCtxt creation --- compiler/rustc_interface/src/limits.rs | 53 +++++++++++++------ compiler/rustc_interface/src/passes.rs | 27 +++------- compiler/rustc_middle/src/queries.rs | 1 + .../feature-gate-large-assignments.rs | 1 + .../feature-gate-large-assignments.stderr | 13 ++++- .../feature-gate-pattern-complexity-limit.rs | 4 ++ ...ature-gate-pattern-complexity-limit.stderr | 13 ++++- 7 files changed, 74 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_interface/src/limits.rs b/compiler/rustc_interface/src/limits.rs index a0791bb22df6d..a97aef1a66058 100644 --- a/compiler/rustc_interface/src/limits.rs +++ b/compiler/rustc_interface/src/limits.rs @@ -8,28 +8,51 @@ //! Users can override these limits via an attribute on the crate like //! `#![recursion_limit="22"]`. This pass just looks for those attributes. +use rustc_ast::{self as ast, DUMMY_NODE_ID}; +use rustc_attr_ir::target::Target; use rustc_attr_ir::{Attribute, find_attr}; -use rustc_middle::query::Providers; +use rustc_attr_parsing::{AttributeParser, ShouldEmit}; +use rustc_feature::Features; use rustc_session::{Limits, Session}; +use rustc_span::{DUMMY_SP, sym}; use rustc_structures::Limit; -pub(crate) fn provide(providers: &mut Providers) { - providers.limits = |tcx, ()| { - let attrs = tcx.hir_krate_attrs(); - Limits { - recursion_limit: get_recursion_limit(tcx.hir_krate_attrs(), tcx.sess), - move_size_limit: find_attr!(attrs, MoveSizeLimit { limit } => *limit) - .unwrap_or(Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0))), - type_length_limit: find_attr!(attrs, TypeLengthLimit { limit } => *limit) - .unwrap_or(Limit::new(2usize.pow(24))), - pattern_complexity_limit: find_attr!(attrs, PatternComplexityLimit { limit } => *limit) - .unwrap_or(Limit::unlimited()), - } +pub fn get_limits(krate_attrs: &[ast::Attribute], sess: &Session, features: &Features) -> Limits { + let attrs = AttributeParser::parse_limited_all( + sess, + &krate_attrs, + Some(&|attr| { + attr.has_any_name(&[ + sym::recursion_limit, + sym::move_size_limit, + sym::type_length_limit, + sym::pattern_complexity_limit, + ]) + }), + Target::Crate, + DUMMY_SP, + DUMMY_NODE_ID, + Some(features), + // errors are fatal here, but lints aren't. + // If things aren't fatal we continue, and will parse this again. + // That makes the same lint trigger again. + // So, no lints here to avoid duplicates. + ShouldEmit::EarlyFatal { also_emit_lints: false }, + None, + ); + let attrs = &attrs; + Limits { + recursion_limit: get_recursion_limit(attrs, sess), + move_size_limit: find_attr!(attrs, MoveSizeLimit { limit } => *limit) + .unwrap_or(Limit::new(sess.opts.unstable_opts.move_size_limit.unwrap_or(0))), + type_length_limit: find_attr!(attrs, TypeLengthLimit { limit } => *limit) + .unwrap_or(Limit::new(2usize.pow(24))), + pattern_complexity_limit: find_attr!(attrs, PatternComplexityLimit { limit } => *limit) + .unwrap_or(Limit::unlimited()), } } -// This one is separate because it must be read prior to macro expansion. -pub(crate) fn get_recursion_limit(attrs: &[Attribute], sess: &Session) -> Limit { +fn get_recursion_limit(attrs: &[Attribute], sess: &Session) -> Limit { let limit_from_crate = find_attr!(attrs, RecursionLimit { limit } => limit.0).unwrap_or(128); Limit::new( sess.opts diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8975f01de5216..b828a609bc02a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -44,7 +44,7 @@ use rustc_session::{IncrCompSession, Session}; use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, }; -use rustc_structures::{CrateType, Limit}; +use rustc_structures::CrateType; use rustc_trait_selection::{solve, traits}; use tracing::{info, instrument}; @@ -199,7 +199,7 @@ fn configure_and_expand( } // Create the config for macro expansion - let recursion_limit = get_recursion_limit(pre_configured_attrs, sess); + let recursion_limit = tcx.recursion_limit(); let cfg = rustc_expand::expand::ExpansionConfig { crate_name, features, @@ -899,7 +899,6 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { providers.queries.env_var_os = env_var_os; providers.queries.proc_macro_decls_static = |tcx, _| tcx.hir_crate_items(()).proc_macro_decls(); rustc_ast_lowering::provide(&mut providers.queries); - limits::provide(&mut providers.queries); rustc_expand_queries::provide(&mut providers.queries); rustc_const_eval::provide(providers); rustc_middle::hir::provide(&mut providers.queries); @@ -1026,11 +1025,13 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( feed.crate_name(crate_name); let feed = tcx.feed_unit_query(); - feed.features_query(tcx.arena.alloc(rustc_expand::config::features( + let features = tcx.arena.alloc(rustc_expand::config::features( tcx.sess, &pre_configured_attrs, crate_name, - ))); + )); + feed.features_query(features); + feed.limits(limits::get_limits(&pre_configured_attrs, tcx.sess, features)); feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); feed.output_filenames(Arc::new(outputs)); @@ -1512,19 +1513,3 @@ pub fn collect_crate_types( fn default_output_for_target(sess: &Session) -> CrateType { if !sess.target.executables { CrateType::StaticLib } else { CrateType::Executable } } - -fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { - let attr = AttributeParser::parse_limited_sym_should_emit( - sess, - &krate_attrs, - &[sym::recursion_limit], - DUMMY_SP, - None, - // errors are fatal here, but lints aren't. - // If things aren't fatal we continue, and will parse this again. - // That makes the same lint trigger again. - // So, no lints here to avoid duplicates. - ShouldEmit::EarlyFatal { also_emit_lints: false }, - ); - crate::limits::get_recursion_limit(attr.as_slice(), sess) -} diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index d0429df43d1dd..4632c3ca1fc12 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2721,6 +2721,7 @@ rustc_queries! { } query limits(key: ()) -> Limits { + feedable desc { "looking up limits" } } diff --git a/tests/ui/feature-gates/feature-gate-large-assignments.rs b/tests/ui/feature-gates/feature-gate-large-assignments.rs index 3c284ca3cafbd..dc37b8735410b 100644 --- a/tests/ui/feature-gates/feature-gate-large-assignments.rs +++ b/tests/ui/feature-gates/feature-gate-large-assignments.rs @@ -1,5 +1,6 @@ // check that `move_size_limit` is feature-gated #![move_size_limit = "42"] //~ ERROR the `move_size_limit` attribute is an experimental feature +//~^ ERROR the `move_size_limit` attribute is an experimental feature fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-large-assignments.stderr b/tests/ui/feature-gates/feature-gate-large-assignments.stderr index 350afcc0d63ad..6ec13a4f5342e 100644 --- a/tests/ui/feature-gates/feature-gate-large-assignments.stderr +++ b/tests/ui/feature-gates/feature-gate-large-assignments.stderr @@ -8,6 +8,17 @@ LL | #![move_size_limit = "42"] = help: add `#![feature(large_assignments)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error[E0658]: the `move_size_limit` attribute is an experimental feature + --> $DIR/feature-gate-large-assignments.rs:3:4 + | +LL | #![move_size_limit = "42"] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #83518 for more information + = help: add `#![feature(large_assignments)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs index 972312a497fa2..5a04749132ce2 100644 --- a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs +++ b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.rs @@ -2,7 +2,11 @@ #![pattern_complexity_limit = "42"] //~^ ERROR: use of an internal attribute [E0658] +//~| ERROR: use of an internal attribute [E0658] //~| NOTE the `pattern_complexity_limit` attribute is an internal implementation detail that will never be stable //~| NOTE: the `pattern_complexity_limit` attribute is used for rustc unit tests +//~| NOTE the `pattern_complexity_limit` attribute is an internal implementation detail that will never be stable +//~| NOTE: the `pattern_complexity_limit` attribute is used for rustc unit tests +//~| NOTE: duplicate diagnostic fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr index 7faf38d842e7f..82c57be8ec4e4 100644 --- a/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr +++ b/tests/ui/feature-gates/feature-gate-pattern-complexity-limit.stderr @@ -8,6 +8,17 @@ LL | #![pattern_complexity_limit = "42"] = note: the `pattern_complexity_limit` attribute is an internal implementation detail that will never be stable = note: the `pattern_complexity_limit` attribute is used for rustc unit tests -error: aborting due to 1 previous error +error[E0658]: use of an internal attribute + --> $DIR/feature-gate-pattern-complexity-limit.rs:3:4 + | +LL | #![pattern_complexity_limit = "42"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `pattern_complexity_limit` attribute is an internal implementation detail that will never be stable + = note: the `pattern_complexity_limit` attribute is used for rustc unit tests + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. From ec9a24815acda76536120e8aabacaeb710ab7b4c Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Thu, 15 Oct 2026 18:48:54 +0200 Subject: [PATCH 3/6] core: hint: fix `unlikely`'s example polarity typo The line did not seem to get updated when the example was likely copy-pasted from the `likely()` one. Thus fix it. Signed-off-by: Miguel Ojeda --- library/core/src/hint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 616bdd9a97b7c..247e1430de3ed 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -704,7 +704,7 @@ pub const fn likely(b: bool) -> bool { /// // Use outside of a branch condition may still influence a nearby branch /// let cond = unlikely(x != 0); /// if cond { -/// println!("this branch is likely to be taken"); +/// println!("this branch is unlikely to be taken"); /// } /// } /// ``` From fd920982ac3a7d526306756bc334112767630082 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Fri, 16 Oct 2026 15:17:43 +0800 Subject: [PATCH 4/6] Add ui test for issue 164169 --- ...-loop-explain-borrow-error-issue-164169.rs | 31 +++++++++++++++++++ ...p-explain-borrow-error-issue-164169.stderr | 24 ++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs create mode 100644 tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr diff --git a/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs new file mode 100644 index 0000000000000..447c00393884f --- /dev/null +++ b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs @@ -0,0 +1,31 @@ +//@ check-fail + +use std::path::PathBuf; + +type Test = Option<(PathBuf, Vec)>; + +fn test1(backend: &mut Vec) -> Result<(), String> { + let mut backend2 = Vec::new(); + for b in backend { + backend2.push(match b { + None => None, + Some((file, bytes)) => Some((file.as_path(), bytes)), + }); + } + + loop { + test2(&mut backend2)?; //~ ERROR E0499 + } +} + +fn test2<'a, P, C>(disks: &'a mut [Option<(P, &'a mut Vec)>]) -> Result { + todo!() +} + +fn test3<'a, P, C>(disks: &'a mut [Option<(P, &'a mut Vec)>]) -> Result { + loop { + test2(disks)?; //~ ERROR E0499 + } +} + +fn main() {} diff --git a/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr new file mode 100644 index 0000000000000..4084ed7e13680 --- /dev/null +++ b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr @@ -0,0 +1,24 @@ +error[E0499]: cannot borrow `backend2` as mutable more than once at a time + --> $DIR/wrong-loop-explain-borrow-error-issue-164169.rs:17:15 + | +LL | backend2.push(match b { + | -------- first borrow used here, in later iteration of loop +... +LL | test2(&mut backend2)?; + | ^^^^^^^^^^^^^ `backend2` was mutably borrowed here in the previous iteration of the loop + +error[E0499]: cannot borrow `*disks` as mutable more than once at a time + --> $DIR/wrong-loop-explain-borrow-error-issue-164169.rs:27:15 + | +LL | fn test3<'a, P, C>(disks: &'a mut [Option<(P, &'a mut Vec)>]) -> Result { + | -- lifetime `'a` defined here +LL | loop { +LL | test2(disks)?; + | ------^^^^^- + | | | + | | `*disks` was mutably borrowed here in the previous iteration of the loop + | argument requires that `*disks` is borrowed for `'a` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. From ad1fd473c5db072c80c793fcab135aaf6e21659c Mon Sep 17 00:00:00 2001 From: xizheyin Date: Fri, 16 Oct 2026 16:22:02 +0800 Subject: [PATCH 5/6] skip wrong loops when explaining borrow errors --- .../rustc_borrowck/src/diagnostics/explain_borrow.rs | 2 +- .../rustc_borrowck/src/region_infer/region_context.rs | 11 +++++++---- .../wrong-loop-explain-borrow-error-issue-164169.rs | 2 +- ...rong-loop-explain-borrow-error-issue-164169.stderr | 8 ++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 706a60d84f402..bdd22ab7a2524 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -610,7 +610,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // loop iteration. In this case, try using the loop terminator location in // `find_sub_region_live_at`. if let Some(loop_terminator_location) = - regioncx.find_loop_terminator_location(borrow.region, body) + regioncx.find_loop_terminator_location(borrow.region, body, location) { region_sub = self .regioncx diff --git a/compiler/rustc_borrowck/src/region_infer/region_context.rs b/compiler/rustc_borrowck/src/region_infer/region_context.rs index 327acb7da3fb8..6f29c42eed249 100644 --- a/compiler/rustc_borrowck/src/region_infer/region_context.rs +++ b/compiler/rustc_borrowck/src/region_infer/region_context.rs @@ -816,16 +816,19 @@ impl<'tcx> RegionInferenceContextInner<'tcx> { &self, r: RegionVid, body: &Body<'_>, + from_location: Location, ) -> Option { let scc = self.constraint_sccs.scc(r); let locations = self.scc_values.locations_outlived_by(scc); for location in locations { - let bb = &body[location.block]; - if let Some(terminator) = &bb.terminator + if from_location.is_predecessor_of(location, body) { + let bb = &body[location.block]; + if let Some(terminator) = &bb.terminator // terminator of a loop should be TerminatorKind::FalseUnwind && let TerminatorKind::FalseUnwind { .. } = terminator.kind - { - return Some(location); + { + return Some(location); + } } } None diff --git a/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs index 447c00393884f..82599a84df661 100644 --- a/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs +++ b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.rs @@ -1,5 +1,5 @@ //@ check-fail - +//@ forbid-output: backend2.push( use std::path::PathBuf; type Test = Option<(PathBuf, Vec)>; diff --git a/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr index 4084ed7e13680..92026d3aedec8 100644 --- a/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr +++ b/tests/ui/borrowck/wrong-loop-explain-borrow-error-issue-164169.stderr @@ -1,11 +1,11 @@ error[E0499]: cannot borrow `backend2` as mutable more than once at a time --> $DIR/wrong-loop-explain-borrow-error-issue-164169.rs:17:15 | -LL | backend2.push(match b { - | -------- first borrow used here, in later iteration of loop -... LL | test2(&mut backend2)?; - | ^^^^^^^^^^^^^ `backend2` was mutably borrowed here in the previous iteration of the loop + | ^^^^^^^^^^^^^ + | | + | `backend2` was mutably borrowed here in the previous iteration of the loop + | first borrow used here, in later iteration of loop error[E0499]: cannot borrow `*disks` as mutable more than once at a time --> $DIR/wrong-loop-explain-borrow-error-issue-164169.rs:27:15 From 7d58ad7df3fe097351ab243368f597001143ce76 Mon Sep 17 00:00:00 2001 From: TechnoPorg Date: Fri, 16 Oct 2026 12:38:15 +0000 Subject: [PATCH 6/6] Add bootstrap step to build the TPDE plugin --- .gitmodules | 4 + bootstrap.example.toml | 7 +- src/bootstrap/src/core/build_steps/compile.rs | 20 ++ src/bootstrap/src/core/build_steps/llvm.rs | 179 +++++++++++++++++- src/bootstrap/src/core/builder/mod.rs | 5 +- src/bootstrap/src/core/config/config.rs | 6 + src/bootstrap/src/core/config/toml/llvm.rs | 2 + src/bootstrap/src/utils/change_tracker.rs | 5 + src/tools/tidy/config/ruff.toml | 2 + src/tools/tidy/src/walk.rs | 1 + src/tpde | 1 + 11 files changed, 226 insertions(+), 6 deletions(-) create mode 160000 src/tpde diff --git a/.gitmodules b/.gitmodules index 9d04fae237505..bbd05e8b2de2b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -47,3 +47,7 @@ path = src/gcc url = https://github.com/rust-lang/gcc.git shallow = true +[submodule "src/tpde"] + path = src/tpde + url = https://github.com/rust-lang/tpde.git + shallow = true diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 34ff4eb623917..571ddb67e83c8 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -103,9 +103,12 @@ # Whether to build Enzyme as AutoDiff backend. #llvm.enzyme = false -# Whether to build LLVM with support for it's gpu offload runtime. +# Whether to build LLVM with support for its gpu offload runtime. #llvm.offload = false +# Whether to build TPDE as an experimental alternative LLVM backend. +#llvm.tpde = false + # Absolute path to the directory containing ClangConfig.cmake #llvm.offload-clang-dir = "" @@ -842,7 +845,7 @@ # sysroot. #rust.llvm-tools = true -# Indicates whether the `self-contained` llvm-bitcode-linker, will be made available +# Indicates whether the `self-contained` llvm-bitcode-linker will be made available # in the sysroot. It is required for running nvptx tests. #rust.llvm-bitcode-linker = false diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 3aded58123525..158a54f14d30b 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2545,6 +2545,26 @@ impl CommandLineStep for Assemble { maybe_install_llvm_bitcode_linker(); + if builder.config.llvm_tpde { + debug!("`llvm_tpde` requested"); + // The rustc TPDE integration is Linux-only for now + if !host.contains("linux") { + builder.info(&format!("host target `{host}` not supported by tpde. skipping")); + } else { + // Put TPDE next to librustc_driver.so + let tpde_output = builder.ensure(llvm::Tpde { target: host }); + let target_libdir = + builder.sysroot_target_libdir(target_compiler, target_compiler.host); + let target_dst_lib = + target_libdir.join(tpde_output.plugin_path().file_name().unwrap()); + builder.copy_link( + tpde_output.plugin_path(), + &target_dst_lib, + FileType::NativeLibrary, + ); + } + } + // Ensure that `libLLVM.so` ends up in the newly build compiler directory, // so that it can be found when the newly built `rustc` is run. debug!( diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 4ac0f6ea85679..0e163cb658fed 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -841,7 +841,7 @@ fn try_link_with_in_tree_lld( cfg: &mut cmake::Config, ldflags: &mut LdFlags, ) { - // Apple has it's own ld64 linker, so don't use LLD on Darwin. + // Apple has its own ld64 linker, so don't use LLD on Darwin. if target.contains("apple") { return; } @@ -856,7 +856,8 @@ fn try_link_with_in_tree_lld( } let lld_bin = builder.ensure(Lld { target }).join("bin"); - ldflags.push_all(format!("-B{} -fuse-ld=lld", lld_bin.display())); + // Quote the path to LLD to prevent it from splitting if it contains a space + ldflags.push_all(format!("'-B{}' -fuse-ld=lld", lld_bin.display())); if llvm_output.link_shared() { // LLD in this case needs the LLVM lib, so tell where to look for it. @@ -2278,3 +2279,177 @@ impl Step for FileCheck { llvm_output.root_dir().join("bin").join(exe("FileCheck", self.target)) } } + +/// Result of building TPDE artifacts. +/// +/// Currently only tpde-plugin.so is consumed elsewhere. +#[derive(Clone)] +pub struct TpdeOutput { + tpde_root_dir: PathBuf, + tpde_plugin_path: PathBuf, +} + +impl TpdeOutput { + /// Path to tpde-plugin.so + pub fn plugin_path(&self) -> &Path { + &self.tpde_plugin_path + } +} + +pub struct TpdeBuildInfo { + stamp: BuildStamp, + output: TpdeOutput, +} + +pub enum TpdeBuildStatus { + AlreadyBuilt(TpdeOutput), + ShouldBuild(TpdeBuildInfo), +} + +/// Return build status of TPDE, considering only the locally built TPDE. +/// +/// Calling this function should never attempt to checkout the TPDE submodule. +fn get_locally_built_tpde_build_status( + builder: &Builder<'_>, + target: TargetSelection, +) -> TpdeBuildStatus { + let out_dir = tpde_output_dir(builder, target); + + let res = TpdeOutput { + tpde_plugin_path: out_dir.join("build").join("tpde-llvm").join("tpde-plugin.so"), + tpde_root_dir: out_dir, + }; + + static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); + let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { + generate_smart_stamp_hash( + builder, + &builder.config.src.join("src/tpde"), + builder.in_tree_tpde_info.sha().unwrap_or_default(), + ) + }); + + // Rebuild if build options change + let stamp = BuildStamp::new(&res.tpde_root_dir) + .with_prefix("tpde") + .add_stamp(smart_stamp_hash) + .add_stamp(builder.config.llvm_assertions) + .add_stamp(builder.config.llvm_optimize) + .add_stamp(builder.config.llvm_release_debuginfo); + + if stamp.is_up_to_date() { + if smart_stamp_hash.is_empty() { + builder.info( + "Could not determine the TPDE submodule commit hash. \ + Assuming that a TPDE rebuild is not necessary.", + ); + builder.info(&format!( + "To force TPDE to rebuild, remove the file `{}`", + stamp.path().display() + )); + } + return TpdeBuildStatus::AlreadyBuilt(res); + } + + TpdeBuildStatus::ShouldBuild(TpdeBuildInfo { stamp, output: res }) +} + +/// Output directory of *locally built* TPDE for the given `target`. +/// Should only be used within this module, when building TPDE. +/// Otherwise, you should ensure the `Tpde` step and read its root directory. +fn tpde_output_dir(builder: &Builder<'_>, target: TargetSelection) -> PathBuf { + builder.config.out.join(target).join("tpde") +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct Tpde { + pub target: TargetSelection, +} + +impl CommandLineStep for Tpde { + type Output = TpdeOutput; + + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tpde").alias("tpde") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Tpde { target: run.target }); + } + + /// Compile TPDE for `target`. + fn run(self, builder: &Builder<'_>) -> TpdeOutput { + let target = self.target; + if !target.contains("linux") { + builder.info(&format!("WARNING: host target {target} not supported by TPDE")); + } + let llvm = builder.ensure(Llvm { target }); + if llvm.kind() != LlvmKind::BuiltLocally || !llvm.link_shared() { + // Due to current limitations with the way LLVM configures CMake exports, it is not possible to use CI LLVM. + // FIXME: experiment with LLVM_OMIT_EXPORTS_FROM_CONFIG=OFF and changing library search paths as described in https://github.com/rust-lang/rust/pull/163731#discussion_r4286684608, + // or (preferred) wait until upstream LLVM patches have landed to decouple static exports from dynamic exports in the CMake config + panic!( + "TPDE currently requires building with a locally built LLVM and `llvm.link-shared = true` in bootstrap" + ); + } + builder.config.update_submodule("src/tpde"); + // If TPDE has already been built, we avoid building it again. + let TpdeBuildInfo { stamp, output } = + match get_locally_built_tpde_build_status(builder, target) { + TpdeBuildStatus::AlreadyBuilt(p) => return p, + TpdeBuildStatus::ShouldBuild(m) => m, + }; + + let _guard = builder.msg_unstaged(Kind::Build, "TPDE", target); + t!(stamp.remove()); + let _time = helpers::timeit(builder); + t!(fs::create_dir_all(&output.tpde_root_dir)); + + let mut cfg = cmake::Config::new(builder.src.join("src/tpde")); + + let profile = get_llvm_profile(&builder.config); + let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" }; + + cfg.out_dir(&output.tpde_root_dir) + .profile(profile) + .define("TPDE_ENABLE_ASSERTIONS", assertions) + .define("TPDE_ENABLE_LLVM", "ON") + .define("TPDE_ENABLE_ENCODEGEN", "ON") + .define("TPDE_INCLUDE_TESTS", "OFF") + .define("TPDE_ENABLE_ASAN", "OFF") + .define("TPDE_LOGGING", "OFF") + .define("TPDE_ENABLE_COVERAGE", "OFF") + .define("TPDE_BUILD_DOCS", "OFF") + .define("TPDE_ENABLE_EH", "OFF") + .define("LLVM_DIR", llvm.cmake_dir()); + + let mut ldflags = LdFlags::default(); + try_link_with_in_tree_lld(builder, target, &llvm, &mut cfg, &mut ldflags); + configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]); + + if builder.config.dry_run() { + return output; + } + + cfg.build_target("tpde-plugin.so"); + + // When building TPDE as a shared library on linux, it can contain unexpected debuginfo: + // some can come from the C++ standard library. Unless we're explicitly requesting TPDE to + // be built with debuginfo, strip it away after the fact, to make dist artifacts smaller. + if builder.config.llvm_optimize && !builder.config.llvm_release_debuginfo { + // If the shared library exists in TPDE's `/build/tpde-llvm` folder, strip its + // debuginfo. + crate::core::build_steps::compile::strip_debug(builder, target, output.plugin_path()); + } + + t!(stamp.write()); + + output + } + + fn metadata(&self) -> Option { + Some(StepMetadata::build("tpde", self.target)) + } +} diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index bfb168d057c6d..cc0b32d7bb863 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -807,13 +807,14 @@ impl<'a> Builder<'a> { llvm::Enzyme, llvm::RustOffload, llvm::CrtBeginEnd, + llvm::Tpde, tool::RustdocGUITest, tool::OptimizedDist, tool::CoverageDump, tool::LlvmBitcodeLinker, tool::RustcPerf, tool::WasmComponentLd, - tool::LldWrapper + tool::LldWrapper, ), Kind::Clippy => describe!( clippy::Std, @@ -1011,7 +1012,7 @@ impl<'a> Builder<'a> { dist::BuildManifest, dist::ReproducibleArtifacts, dist::GccDev, - dist::Gcc + dist::Gcc, ), Kind::Install => describe!( install::Docs, diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1e58d86024874..8aaab722545d2 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -177,6 +177,7 @@ pub(crate) struct Config { pub llvm_enable_warnings: bool, pub llvm_ci_mode: LlvmCiMode, pub llvm_build_config: HashMap, + pub llvm_tpde: bool, pub bootstrap_override_lld: BootstrapOverrideLld, pub lld_enabled: bool, @@ -305,6 +306,7 @@ pub(crate) struct Config { pub enzyme_info: channel::GitInfo, pub in_tree_llvm_info: channel::GitInfo, pub in_tree_gcc_info: channel::GitInfo, + pub in_tree_tpde_info: channel::GitInfo, /// rustc/cargo/rustdoc/clippy paths specified in the config file /// Access the `initial_` fields from `Session` to use either the externally configured @@ -656,6 +658,7 @@ impl Config { enable_warnings: llvm_enable_warnings, download_ci_llvm: llvm_download_ci_llvm, build_config: llvm_build_config, + tpde: llvm_tpde, } = toml_llvm.unwrap_or_default(); let Dist { @@ -1366,6 +1369,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to let clippy_info = git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/clippy")); let in_tree_gcc_info = git_info(&exec_ctx, false, &src.join("src/gcc")); let in_tree_llvm_info = git_info(&exec_ctx, false, &src.join("src/llvm-project")); + let in_tree_tpde_info = git_info(&exec_ctx, false, &src.join("src/tpde")); let enzyme_info = git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/enzyme")); let miri_info = git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/miri")); let rust_analyzer_info = @@ -1469,6 +1473,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to hosts, in_tree_gcc_info, in_tree_llvm_info, + in_tree_tpde_info, include_default_paths: flags_include_default_paths, incremental: flags_incremental || rust_incremental == Some(true), jobs: Some(threads_from_config(flags_jobs.or(build_jobs).unwrap_or(0))), @@ -1509,6 +1514,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to llvm_tests: llvm_tests.unwrap_or(false), llvm_thin_lto: llvm_thin_lto.unwrap_or(false), llvm_tools_enabled: rust_llvm_tools.unwrap_or(true), + llvm_tpde: llvm_tpde.unwrap_or(false), llvm_use_libcxx: llvm_use_libcxx.unwrap_or(false), llvm_use_linker, llvm_version_suffix, diff --git a/src/bootstrap/src/core/config/toml/llvm.rs b/src/bootstrap/src/core/config/toml/llvm.rs index d1cd4eb3358a5..2d810f389273e 100644 --- a/src/bootstrap/src/core/config/toml/llvm.rs +++ b/src/bootstrap/src/core/config/toml/llvm.rs @@ -40,6 +40,7 @@ define_config! { enable_warnings: Option = "enable-warnings", download_ci_llvm: Option = "download-ci-llvm", build_config: Option> = "build-config", + tpde: Option = "tpde", } } @@ -119,6 +120,7 @@ pub fn check_incompatible_options_for_ci_llvm( download_ci_llvm: _, build_config, enzyme, + tpde: _, } = ci_llvm_config; err!(current_llvm_config.optimize, optimize); diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 922d969ec1f35..fd794ed34bf68 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -676,4 +676,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "You should now use `x install rust-src` instead of `x install src` to install the standard library source component. If you want to install it as part of a custom `build.tools` set, include `rust-src` in `build.tools.", }, + ChangeInfo { + change_id: 163731, + severity: ChangeSeverity::Info, + summary: "A new option `llvm.tpde` has been added to control whether the TPDE pass plugin should be built as an experimental alternative LLVM backend.", + }, ]; diff --git a/src/tools/tidy/config/ruff.toml b/src/tools/tidy/config/ruff.toml index 4a5aa618be15e..b38006f8e8c0d 100644 --- a/src/tools/tidy/config/ruff.toml +++ b/src/tools/tidy/config/ruff.toml @@ -22,6 +22,7 @@ extend-exclude = [ "compiler/rustc_codegen_gcc", "src/tools/clippy", "src/tools/miri", + "src/tpde", # Hack: CI runs from a subdirectory under the main checkout "../src/doc/nomicon/", "../src/tools/cargo/", @@ -40,6 +41,7 @@ extend-exclude = [ "../compiler/rustc_codegen_gcc", "../src/tools/clippy", "../src/tools/miri", + "../src/tpde", ] [lint] diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs index 7825ebeb5baa6..8f7884ab425f2 100644 --- a/src/tools/tidy/src/walk.rs +++ b/src/tools/tidy/src/walk.rs @@ -25,6 +25,7 @@ pub fn filter_dirs(path: &Path) -> bool { "src/tools/rustc-perf", "src/tools/rustfmt", "src/tools/enzyme", + "src/tpde", "src/doc/book", "src/doc/edition-guide", "src/doc/embedded-book", diff --git a/src/tpde b/src/tpde new file mode 160000 index 0000000000000..9779acf4ada37 --- /dev/null +++ b/src/tpde @@ -0,0 +1 @@ +Subproject commit 9779acf4ada3736e779391da1e4b3369dba08024