diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index c2fd8a79407f0..5f2060a447535 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -22,12 +22,12 @@ use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; +use rustc_session::config::{LtoCli, OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_session::{ BorrowedIncrCompSession, CodegenBackendInit, EarlySession, IncrCompSession, Session, }; use rustc_span::{Symbol, sym}; -use rustc_target::spec::{RelocModel, TlsModel}; +use rustc_target::spec::{RelocModel, SplitDebuginfo, TlsModel}; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::write::{create_informational_target_machine, create_target_machine}; @@ -213,6 +213,43 @@ impl CodegenBackend for LlvmCodegenBackend { } } + // -Zllvm-tpde requires the TPDE pass plugin, which may not be available. If TPDE is used but not available, + // we emit an early error here and abort compilation. + { + match llvm::TpdeWrapper::get_or_init( + &sess.opts.sysroot, + &sess.opts.unstable_opts.llvm_tpde, + ) { + Ok(_) => {} + Err(llvm::TpdeLibraryError::NotFound { err }) => { + sess.dcx().emit_fatal(crate::diagnostics::TpdeComponentMissing { err }); + } + Err(llvm::TpdeLibraryError::LoadFailed { err }) => { + sess.dcx().emit_fatal(crate::diagnostics::TpdeComponentUnavailable { err }); + } + } + + if sess.opts.unstable_opts.llvm_tpde.is_some() { + if !matches!(sess.opts.optimize, OptLevel::No | OptLevel::Less) { + sess.dcx().emit_warn(crate::diagnostics::TpdeHighOptLevel); + } + + if sess.target.requires_lto + || !matches!(sess.early_lto(), LtoCli::No | LtoCli::Unspecified) + { + sess.dcx().emit_fatal(crate::diagnostics::TpdeLtoUsed); + } + + // JUSTIFICATION: the session doesn't exist at this point. + #[allow(rustc::bad_opt_access)] + if sess.opts.cg.split_debuginfo.unwrap_or(sess.target.split_debuginfo) + != SplitDebuginfo::Off + { + sess.dcx().emit_fatal(crate::diagnostics::TpdeSplitDebugInfoUsed); + } + } + } + // Intrinsics whose fallback body will not be used by the LLVM backend. let replaced_intrinsics = { #[rustfmt::skip] diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 872c022373d45..ef8ca811d245b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -40,7 +40,7 @@ use crate::diagnostics::{ UnsupportedCompression, WithLlvmError, WriteBytecode, }; use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; -use crate::llvm::{self, DiagnosticInfo}; +use crate::llvm::{self, DiagnosticInfo, TpdeWrapper}; use crate::type_::llvm_type_ptr; use crate::{LlvmCodegenBackend, ModuleLlvm, attributes, base, common, llvm_util}; @@ -71,6 +71,8 @@ fn write_output_file<'ll>( } else { std::ptr::null() }; + + let tpde = TpdeWrapper::get_instance(); let result = unsafe { llvm::LLVMRustWriteOutputFile( target, @@ -80,6 +82,7 @@ fn write_output_file<'ll>( file_type, verify_llvm_ir, no_builtins, + tpde.options().clone(), ) }; diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index 094b683ded56b..b42862c05f3bb 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -107,6 +107,33 @@ pub(crate) struct LtoBitcodeFromRlib { pub err: String, } +#[derive(Diagnostic)] +#[diag("failed to load the TPDE backend: {$err}")] +pub(crate) struct TpdeComponentUnavailable { + pub err: String, +} + +#[derive(Diagnostic)] +#[diag("TPDE backend not found in the sysroot: {$err}")] +pub(crate) struct TpdeComponentMissing { + pub err: String, +} + +#[derive(Diagnostic)] +#[diag("using -Zllvm-tpde with optimization levels higher than 1 is not recommended")] +#[note( + "compilation with TPDE at higher opt levels is more likely to fail, and to have worse runtime performance than LLVM even if it succeeds" +)] +pub(crate) struct TpdeHighOptLevel; + +#[derive(Diagnostic)] +#[diag("using -Zllvm-tpde with LTO enabled is not supported")] +pub(crate) struct TpdeLtoUsed; + +#[derive(Diagnostic)] +#[diag("using -Zllvm-tpde with split debuginfo is not supported")] +pub(crate) struct TpdeSplitDebugInfoUsed; + #[derive(Diagnostic)] pub(crate) enum LlvmError<'a> { #[diag("could not write output to {$path}")] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index b109772eb501b..d87429279050a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -25,8 +25,7 @@ use super::debuginfo::{ DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIFile, DIFlags, DILocation, DISPFlags, DIScope, DISubprogram, DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; -use crate::llvm; -use crate::llvm::MetadataKindId; +use crate::llvm::{self, LLVMRustTpdeOptions, MetadataKindId}; use crate::macros::TryFromU32; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, @@ -2439,6 +2438,7 @@ unsafe extern "C" { FileType: FileType, VerifyIR: bool, DisableSimplifyLibCalls: bool, + TpdeOptions: LLVMRustTpdeOptions, ) -> LLVMRustResult; pub(crate) fn LLVMRustOptimize<'a>( M: &'a Module, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index e189dc758d8e7..0f0394f4d9c93 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -21,9 +21,11 @@ pub(crate) mod enzyme_ffi; mod ffi; mod metadata_kind; pub(crate) mod offload_ffi; +pub(crate) mod tpde_ffi; pub(crate) use self::enzyme_ffi::*; pub(crate) use self::offload_ffi::*; +pub(crate) use self::tpde_ffi::*; impl LLVMRustResult { pub(crate) fn into_result(self) -> Result<(), ()> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/tpde_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/tpde_ffi.rs new file mode 100644 index 0000000000000..5beeaa8a9df0a --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/llvm/tpde_ffi.rs @@ -0,0 +1,125 @@ +use std::path::PathBuf; +use std::sync::OnceLock; + +use rustc_session::config::{self, Sysroot, host_tuple}; +use rustc_session::filesearch; + +unsafe extern "C" { + type LLVMPassPluginLibraryInfo; +} + +type LLVMGetPassPluginInfoFn = unsafe extern "C" fn() -> LLVMPassPluginLibraryInfo; + +/// LLVMRustTpdeMode +#[repr(C)] +#[derive(Clone)] +pub(crate) enum LLVMRustTpdeMode { + None, + Only, + Try, +} + +/// LLVMRustTpdeOptions +#[repr(C)] +#[derive(Clone)] +pub(crate) struct LLVMRustTpdeOptions { + mode: LLVMRustTpdeMode, + // `None` if mode is `LLVMRustTpdeMode::None` + plugin: Option, +} + +pub(crate) struct TpdeWrapper { + options: LLVMRustTpdeOptions, + // Keep the dynamic library loaded while the function pointers are used. + _lib: Option, +} + +static TPDE_INSTANCE: OnceLock = OnceLock::new(); + +#[derive(Debug)] +pub(crate) enum TpdeLibraryError { + NotFound { err: String }, + LoadFailed { err: String }, +} + +impl From for TpdeLibraryError { + fn from(err: libloading::Error) -> Self { + Self::LoadFailed { err: format!("{err:?}") } + } +} + +impl TpdeWrapper { + /// Initialize TpdeWrapper with the given sysroot and mode if not already initialized. + /// Safe to call multiple times - subsequent calls are no-ops due to OnceLock. + pub(crate) fn get_or_init( + sysroot: &rustc_session::config::Sysroot, + mode: &Option, + ) -> Result<&'static Self, TpdeLibraryError> { + let wrapper = TPDE_INSTANCE.get_or_try_init(|| { + let w = match mode { + Some(mode) => Self::call_dynamic(sysroot, mode)?, + None => Self { + options: LLVMRustTpdeOptions { mode: LLVMRustTpdeMode::None, plugin: None }, + _lib: None, + }, + }; + Ok::<_, TpdeLibraryError>(w) + })?; + + Ok(wrapper) + } + + /// Get the TpdeWrapper instance. Panics if not initialized. + pub(crate) fn get_instance() -> &'static Self { + TPDE_INSTANCE + .get() + .expect("TpdeWrapper not initialized. Call get_or_init with sysroot first.") + } + + pub(crate) fn options(&self) -> &LLVMRustTpdeOptions { + &self.options + } + + fn call_dynamic( + sysroot: &rustc_session::config::Sysroot, + mode: &config::LlvmTpde, + ) -> Result { + let mode = match mode { + config::LlvmTpde::Only => LLVMRustTpdeMode::Only, + config::LlvmTpde::Try => LLVMRustTpdeMode::Try, + }; + let tpde_path = Self::get_tpde_path(sysroot)?; + let lib = unsafe { libloading::Library::new(tpde_path)? }; + let llvm_get_pass_plugin_info = + *unsafe { lib.get::(b"llvmGetPassPluginInfo\0")? }; + let options = LLVMRustTpdeOptions { mode, plugin: Some(llvm_get_pass_plugin_info) }; + Ok(Self { options, _lib: Some(lib) }) + } + + fn get_tpde_path(sysroot: &Sysroot) -> Result { + let path_buf = sysroot + .all_paths() + .map(|sysroot_path| { + filesearch::make_target_lib_path(sysroot_path, host_tuple()) + .join("lib") + .with_file_name("tpde-plugin") + .with_extension(std::env::consts::DLL_EXTENSION) + }) + .find(|f| f.exists()) + .ok_or_else(|| { + let candidates = sysroot + .all_paths() + .map(|p| p.join("lib").display().to_string()) + .collect::>() + .join("\n* "); + TpdeLibraryError::NotFound { + err: format!( + "failed to find the `tpde-plugin` shared library \ + in the sysroot candidates:\n* {candidates}" + ), + } + })?; + + Ok(path_buf) + } +} diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index fd54da09bdca6..a2ae6f8399360 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -14,10 +14,10 @@ use rustc_session::config::{ CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, FmtDebug, FunctionReturn, IncrementalStateAssertion, InliningThreshold, Input, InstrumentCoverage, InstrumentMcount, InstrumentMcountOpts, InstrumentXRay, LinkSelfContained, - LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, - OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, - Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, - WasiExecModel, build_session_options, rustc_optgroups, + LinkerPluginLto, LlvmTpde, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, + Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, + PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, + SymbolManglingVersion, WasiExecModel, build_session_options, rustc_optgroups, }; use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib}; @@ -846,6 +846,8 @@ fn test_unstable_options_tracking_hash() { tracked!(lint_llvm_ir, true); tracked!(llvm_module_flag, vec![("bar".to_string(), 123, "max".to_string())]); tracked!(llvm_plugins, vec![String::from("plugin_name")]); + tracked!(llvm_tpde, Some(LlvmTpde::Only)); + tracked!(llvm_tpde, Some(LlvmTpde::Try)); tracked!(llvm_writable, true); tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); tracked!(maximal_hir_to_mir_coverage, true); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 1b18b1f06674c..31814f6f2e426 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -465,11 +465,23 @@ static CodeGenFileType fromRust(LLVMRustFileType Type) { } } -extern "C" LLVMRustResult -LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, - const char *Path, const char *DwoPath, - LLVMRustFileType RustFileType, bool VerifyIR, - bool DisableSimplifyLibCalls) { +extern "C" typedef llvm::PassPluginLibraryInfo (*LLVMGetPassPluginInfo)(); + +enum class LLVMRustTpdeMode { + None, + Only, + Try, +}; + +struct LLVMRustTpdeOptions { + LLVMRustTpdeMode mode; + LLVMGetPassPluginInfo plugin; +}; + +extern "C" LLVMRustResult LLVMRustWriteOutputFile( + LLVMTargetMachineRef Target, LLVMModuleRef M, const char *Path, + const char *DwoPath, LLVMRustFileType RustFileType, bool VerifyIR, + bool DisableSimplifyLibCalls, LLVMRustTpdeOptions TpdeOptions) { std::unique_ptr PM = std::make_unique(); @@ -507,9 +519,25 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, } // TargetMachine::addPassesToEmitFile stores pointers to the output streams - // in a couple of places inside of the object. Explicitly delete the PM after - // we call run() to avoid dangling references. + // in a couple of places inside of the object. Explicitly delete the PM + // after we call run() to avoid dangling references. auto BOS = buffer_ostream(OS); + + if (TpdeOptions.mode != LLVMRustTpdeMode::None) { + assert( + TpdeOptions.plugin && + "rustc_codegen_llvm must set TpdeOptions.plugin if using -Zllvm-tpde"); + llvm::PassPluginLibraryInfo plugin = TpdeOptions.plugin(); + if (plugin.PreCodeGenCallback(*unwrap(M), *unwrap(Target), FileType, BOS)) { + return LLVMRustResult::Success; + } else if (TpdeOptions.mode == LLVMRustTpdeMode::Only) { + std::string error = "Failed to compile module " + + unwrap(M)->getModuleIdentifier() + " with TPDE"; + LLVMRustSetLastError(error.c_str()); + return LLVMRustResult::Failure; + } + } + if (DwoPath) { auto DOS = raw_fd_ostream(DwoPath, EC, sys::fs::OF_None); EC.clear(); diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index 404404bbc1cd0..3f6d89ba9ad8c 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -138,6 +138,7 @@ const ALLOWED_CAPITALIZED_WORDS: &[&str] = &[ "NaNs", "OK", "Rust", + "TPDE", "ThinLTO", "Unicode", "VS", diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 96d4e4ad82468..7f8f18817a3d5 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3373,6 +3373,14 @@ impl PpMode { } } +#[derive(Clone, Hash, PartialEq, Eq, Debug)] +pub enum LlvmTpde { + /// Attempt to compile with TPDE-LLVM, but fall back to LLVM if unsupported LLVM IR is encountered + Try, + /// Attempt to compile with TPDE-LLVM, and bail if unsupported LLVM IR is encountered + Only, +} + #[derive(Clone, Hash, PartialEq, Eq, Debug)] pub enum WasiExecModel { Command, @@ -3423,7 +3431,7 @@ pub(crate) mod dep_tracking { AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CodegenRetagOptions, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount, - InstrumentMcountOpts, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, + InstrumentMcountOpts, InstrumentXRay, LinkerPluginLto, LlvmTpde, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, PointerAuthOption, Polonius, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, @@ -3496,6 +3504,7 @@ pub(crate) mod dep_tracking { InstrumentXRay, LanguageIdentifier, LinkerPluginLto, + LlvmTpde, LocationDetail, LtoCli, MergeFunctions, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 9ac479e8abe51..90ecddfdfa60e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -904,6 +904,7 @@ mod desc { pub(crate) const parse_target_feature: &str = parse_string; pub(crate) const parse_terminal_url: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `auto`"; + pub(crate) const parse_llvm_tpde: &str = "either no value or one of: `try` (default), `only`"; pub(crate) const parse_wasi_exec_model: &str = "either `command` or `reactor`"; pub(crate) const parse_split_debuginfo: &str = "one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)"; @@ -1901,6 +1902,15 @@ pub mod parse { true } + pub(crate) fn parse_llvm_tpde(slot: &mut Option, v: Option<&str>) -> bool { + *slot = match v { + Some("try") | None => Some(LlvmTpde::Try), + Some("only") => Some(LlvmTpde::Only), + _ => return false, + }; + true + } + pub(crate) fn parse_symbol_mangling_version( slot: &mut Option, v: Option<&str>, @@ -2951,6 +2961,7 @@ written to standard error output)"), #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] tls_model: Option = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), + llvm_tpde: Option = (None, parse_llvm_tpde, [TRACKED], "enable the experimental TPDE-LLVM codegen backend for debug builds"), trace_macros: bool = (false, parse_bool, [UNTRACKED], "for every macro invocation, print its name and arguments (default: no)"), track_diagnostics: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 030617b0db56a..813762d09c201 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -2447,6 +2447,7 @@ impl CommandLineStep for Tpde { } cfg.build_target("tpde-plugin.so"); + cfg.build(); // 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 diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 563c38f52fb7a..971c679133b16 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2624,6 +2624,10 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--has-offload"); } + if builder.sess.config.llvm_tpde { + cmd.arg("--has-tpde"); + } + if builder.config.cmd.bless() { cmd.arg("--bless"); } diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index ed6c8b9f93951..1f5578cf4df65 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -212,6 +212,7 @@ The following directives will check rustc build settings and target settings: - `needs-backends` — only runs the test if current codegen backend is listed. - `needs-offload` — ignores if our LLVM backend was not built with offload support. - `needs-enzyme` — ignores if our Enzyme submodule was not built. +- `needs-tpde` — ignores if the TPDE-LLVM codegen backend was not built The following directives will check LLVM support: diff --git a/src/doc/unstable-book/src/compiler-flags/llvm-tpde.md b/src/doc/unstable-book/src/compiler-flags/llvm-tpde.md new file mode 100644 index 0000000000000..24b8eeef6585c --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/llvm-tpde.md @@ -0,0 +1,12 @@ +# `llvm-tpde` + +The tracking issue for this feature is: None. + +--- + +The `-Zllvm-tpde[=VALUE]` compiler flag controls whether TPDE-LLVM is used as the codegen backend for debug builds instead of LLVM. If not set, LLVM is used. + +Possible values are: + +- `try` (default): attempt to compile with TPDE-LLVM, but fall back to LLVM if unsupported IR is encountered +- `only`: attempt to compile with TPDE-LLVM, and bail if unsupported IR is encountered diff --git a/src/tools/compiletest/src/cli.rs b/src/tools/compiletest/src/cli.rs index 893f5b76724b8..778d53f1d8d4f 100644 --- a/src/tools/compiletest/src/cli.rs +++ b/src/tools/compiletest/src/cli.rs @@ -239,6 +239,8 @@ struct Args { /// Run tests that require offload. #[arg(long)] has_offload: bool, + #[arg(long)] + has_tpde: bool, /// Whether rustc was built with debug assertions. #[arg(long)] with_rustc_debug_assertions: bool, @@ -455,6 +457,7 @@ pub(crate) fn parse_config(args: Vec) -> Config { has_enzyme: args.has_enzyme, has_offload: args.has_offload, + has_tpde: args.has_tpde, host: args.host, host_compile_lib_path: make_absolute(args.compile_lib_path), host_linker: args.host_linker, diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index a60b657e96ab5..c25fe8e0a2f8b 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -682,6 +682,9 @@ pub(crate) struct Config { /// Whether to run `offload` autodiff tests. pub(crate) has_offload: bool, + /// Whether to run tests with the `tpde` LLVM backend + pub(crate) has_tpde: bool, + /// The current Rust channel info. /// /// FIXME: treat this more carefully; "stable", "beta" and "nightly" are definitely valid, but diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 7af285775380c..e86e23a9505dd 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -208,6 +208,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-target-has-atomic", "needs-target-std", "needs-threads", + "needs-tpde", "needs-unwind", "needs-wasmtime", "needs-xray", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index ce67b11fba211..6275b7ca2879c 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -239,6 +239,11 @@ pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditio condition: config.has_offload && config.default_codegen_backend.is_llvm(), ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", }, + Need { + name: "needs-tpde", + condition: config.has_tpde && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when TPDE-LLVM is disabled or LLVM is not the default codegen backend", + }, Need { name: "needs-run-enabled", condition: config.run_enabled(), diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index 4e1d1138b3168..c44ef5991e56f 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -121,6 +121,7 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { rustfix_coverage: Default::default(), has_enzyme: Default::default(), has_offload: Default::default(), + has_tpde: Default::default(), channel: Default::default(), git_hash: Default::default(), cc: Default::default(), diff --git a/tests/ui/README.md b/tests/ui/README.md index ded2ef7496303..6fb4d27b6dbea 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1410,6 +1410,10 @@ Broad category for parallelism and multi-threaded tests, including attempting to Exercises [tool attributes](https://doc.rust-lang.org/reference/attributes.html#tool-attributes). +## `tests/ui/tpde` + +Exercises the experimental TPDE-LLVM codegen backend. + ## `tests/ui/track-diagnostics/` Exercises `#[track_caller]` and `-Z track-diagnostics`. diff --git a/tests/ui/tpde/check_option.invalid.stderr b/tests/ui/tpde/check_option.invalid.stderr new file mode 100644 index 0000000000000..2419c47071422 --- /dev/null +++ b/tests/ui/tpde/check_option.invalid.stderr @@ -0,0 +1,2 @@ +error: incorrect value `invalid` for unstable option `llvm-tpde` - either no value or one of: `try` (default), `only` was expected + diff --git a/tests/ui/tpde/check_option.rs b/tests/ui/tpde/check_option.rs new file mode 100644 index 0000000000000..cf4c8360e73c8 --- /dev/null +++ b/tests/ui/tpde/check_option.rs @@ -0,0 +1,16 @@ +//@ revisions: implicit try only invalid +//@ needs-tpde +// +//@[implicit] build-pass +//@[try] build-pass +//@[only] build-pass +//@[invalid] check-fail +// +//@[implicit] compile-flags: -Zllvm-tpde +//@[try] compile-flags: -Zllvm-tpde=try +//@[only] compile-flags: -Zllvm-tpde=only +//@[invalid] compile-flags: -Zllvm-tpde=invalid + +//[invalid]~? ERROR: incorrect value `invalid` for unstable option `llvm-tpde` - either no value or one of: `try` (default), `only` was expected + +fn main() {} diff --git a/tests/ui/tpde/llvm_fallback.only.stderr b/tests/ui/tpde/llvm_fallback.only.stderr new file mode 100644 index 0000000000000..d64dc59ee159f --- /dev/null +++ b/tests/ui/tpde/llvm_fallback.only.stderr @@ -0,0 +1,4 @@ +error: could not write output to $TEST_BUILD_DIR/llvm_fallback.llvm_fallback.bef9b9dab1dfd663-cgu.0.rcgu.o: Failed to compile module llvm_fallback.bef9b9dab1dfd663-cgu.0 with TPDE + +error: aborting due to 1 previous error + diff --git a/tests/ui/tpde/llvm_fallback.rs b/tests/ui/tpde/llvm_fallback.rs new file mode 100644 index 0000000000000..7a071db2c26e3 --- /dev/null +++ b/tests/ui/tpde/llvm_fallback.rs @@ -0,0 +1,22 @@ +//@ revisions: try only +//@ needs-tpde +// +//@[try] build-pass +//@[only] build-fail +// +//@[try] compile-flags: -Zllvm-tpde +//@[only] compile-flags: -Zllvm-tpde=only + +//[only]~? ERROR: could not write output to $TEST_BUILD_DIR/llvm_fallback.llvm_fallback.bef9b9dab1dfd663-cgu.0.rcgu.o: Failed to compile module llvm_fallback.bef9b9dab1dfd663-cgu.0 with TPDE + +// NOTE: if TPDE gains f16 support, this test will need to be adjusted + +#![feature(f16)] + +fn main() { + if std::hint::black_box(0f16) == std::hint::black_box(7f16) { + println!("same"); + } else { + println!("different"); + } +} diff --git a/tests/ui/tpde/no_lto.fat.stderr b/tests/ui/tpde/no_lto.fat.stderr new file mode 100644 index 0000000000000..748f266213ba4 --- /dev/null +++ b/tests/ui/tpde/no_lto.fat.stderr @@ -0,0 +1,2 @@ +error: using -Zllvm-tpde with LTO enabled is not supported + diff --git a/tests/ui/tpde/no_lto.rs b/tests/ui/tpde/no_lto.rs new file mode 100644 index 0000000000000..ad0323bf5a8fa --- /dev/null +++ b/tests/ui/tpde/no_lto.rs @@ -0,0 +1,16 @@ +//@ revisions: no unspecified thin fat +//@ needs-tpde +//@ compile-flags: -Zllvm-tpde +// +//@[no] build-pass +//@[unspecified] build-pass +//@[thin] check-fail +//@[fat] check-fail +// +//@[no] compile-flags: -Clto=no +//@[thin] compile-flags: -Clto=thin +//@[fat] compile-flags: -Clto=fat + +//[thin,fat]~? ERROR: using -Zllvm-tpde with LTO enabled is not supported + +fn main() {} diff --git a/tests/ui/tpde/no_lto.thin.stderr b/tests/ui/tpde/no_lto.thin.stderr new file mode 100644 index 0000000000000..748f266213ba4 --- /dev/null +++ b/tests/ui/tpde/no_lto.thin.stderr @@ -0,0 +1,2 @@ +error: using -Zllvm-tpde with LTO enabled is not supported + diff --git a/tests/ui/tpde/no_split_debuginfo.packed.stderr b/tests/ui/tpde/no_split_debuginfo.packed.stderr new file mode 100644 index 0000000000000..42b6f8bae9aaa --- /dev/null +++ b/tests/ui/tpde/no_split_debuginfo.packed.stderr @@ -0,0 +1,2 @@ +error: using -Zllvm-tpde with split debuginfo is not supported + diff --git a/tests/ui/tpde/no_split_debuginfo.rs b/tests/ui/tpde/no_split_debuginfo.rs new file mode 100644 index 0000000000000..a2cc9c45773aa --- /dev/null +++ b/tests/ui/tpde/no_split_debuginfo.rs @@ -0,0 +1,16 @@ +//@ revisions: off unspecified packed unpacked +//@ needs-tpde +//@ compile-flags: -Zllvm-tpde +// +//@[off] build-pass +//@[unspecified] build-pass +//@[packed] check-fail +//@[unpacked] check-fail +// +//@[off] compile-flags: -Csplit-debuginfo=off +//@[packed] compile-flags: -Csplit-debuginfo=packed +//@[unpacked] compile-flags: -Csplit-debuginfo=unpacked + +//[packed,unpacked]~? ERROR: using -Zllvm-tpde with split debuginfo is not supported + +fn main() {} diff --git a/tests/ui/tpde/no_split_debuginfo.unpacked.stderr b/tests/ui/tpde/no_split_debuginfo.unpacked.stderr new file mode 100644 index 0000000000000..42b6f8bae9aaa --- /dev/null +++ b/tests/ui/tpde/no_split_debuginfo.unpacked.stderr @@ -0,0 +1,2 @@ +error: using -Zllvm-tpde with split debuginfo is not supported + diff --git a/tests/ui/tpde/opt_level.rs b/tests/ui/tpde/opt_level.rs new file mode 100644 index 0000000000000..d8fdb15e4d1a2 --- /dev/null +++ b/tests/ui/tpde/opt_level.rs @@ -0,0 +1,14 @@ +//@ revisions: zero one two three +//@ needs-tpde +//@ build-pass +//@ compile-flags: -Zllvm-tpde +// +//@[zero] compile-flags: -Copt-level=0 +//@[one] compile-flags: -Copt-level=1 +//@[two] compile-flags: -Copt-level=2 +//@[three] compile-flags: -Copt-level=3 + +//[two,three]~? WARN: using -Zllvm-tpde with optimization levels higher than 1 is not recommended +//[two,three]~? NOTE: compilation with TPDE at higher opt levels is more likely to fail, and to have worse runtime performance than LLVM even if it succeeds + +fn main() {} diff --git a/tests/ui/tpde/opt_level.three.stderr b/tests/ui/tpde/opt_level.three.stderr new file mode 100644 index 0000000000000..98295286f38d5 --- /dev/null +++ b/tests/ui/tpde/opt_level.three.stderr @@ -0,0 +1,6 @@ +warning: using -Zllvm-tpde with optimization levels higher than 1 is not recommended + | + = note: compilation with TPDE at higher opt levels is more likely to fail, and to have worse runtime performance than LLVM even if it succeeds + +warning: 1 warning emitted + diff --git a/tests/ui/tpde/opt_level.two.stderr b/tests/ui/tpde/opt_level.two.stderr new file mode 100644 index 0000000000000..98295286f38d5 --- /dev/null +++ b/tests/ui/tpde/opt_level.two.stderr @@ -0,0 +1,6 @@ +warning: using -Zllvm-tpde with optimization levels higher than 1 is not recommended + | + = note: compilation with TPDE at higher opt levels is more likely to fail, and to have worse runtime performance than LLVM even if it succeeds + +warning: 1 warning emitted +