diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 24ba28bbc82c2..514edf4653bb4 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -262,10 +262,10 @@ impl CodegenBackend for LlvmCodegenBackend { |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) } - fn print(&self, req: PrintRequest, sess: &Session) { + fn print(&self, req: PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) { match req { PrintRequest::RelocationModels => { - println!("Available relocation models:"); + writeln!(out, "Available relocation models:"); for name in &[ "static", "pic", @@ -276,26 +276,27 @@ impl CodegenBackend for LlvmCodegenBackend { "ropi-rwpi", "default", ] { - println!(" {}", name); + writeln!(out, " {}", name); } - println!(); + writeln!(out); } PrintRequest::CodeModels => { - println!("Available code models:"); + writeln!(out, "Available code models:"); for name in &["tiny", "small", "kernel", "medium", "large"] { - println!(" {}", name); + writeln!(out, " {}", name); } - println!(); + writeln!(out); } PrintRequest::TlsModels => { - println!("Available TLS models:"); + writeln!(out, "Available TLS models:"); for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] { - println!(" {}", name); + writeln!(out, " {}", name); } - println!(); + writeln!(out); } PrintRequest::StackProtectorStrategies => { - println!( + writeln!( + out, r#"Available stack protector strategies: all Generate stack canaries in all functions. @@ -319,7 +320,7 @@ impl CodegenBackend for LlvmCodegenBackend { "# ); } - req => llvm_util::print(req, sess), + req => llvm_util::print(req, out, sess), } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e1dfc1b2dd6fa..3b857164a27e1 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2281,7 +2281,12 @@ extern "C" { pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; - pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine, cpu: *const c_char); + pub fn LLVMRustPrintTargetCPUs( + T: &TargetMachine, + cpu: *const c_char, + print: unsafe extern "C" fn(out: *mut c_void, string: *const c_char, len: usize), + out: *mut c_void, + ); pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; pub fn LLVMRustGetTargetFeature( T: &TargetMachine, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 03be0654b50bb..e2d087e3deab0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,6 +8,7 @@ use libc::c_int; use rustc_codegen_ssa::target_features::{ supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES, }; +use rustc_codegen_ssa::traits::PrintBackendInfo; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; @@ -16,8 +17,8 @@ use rustc_session::config::PrintRequest; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; -use std::ffi::{CStr, CString}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::path::Path; use std::ptr; use std::slice; @@ -350,7 +351,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { ret } -fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { +fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &llvm::TargetMachine) { let mut llvm_target_features = llvm_target_features(tm); let mut known_llvm_target_features = FxHashSet::<&'static str>::default(); let mut rustc_target_features = supported_target_features(sess) @@ -383,24 +384,24 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { .max() .unwrap_or(0); - println!("Features supported by rustc for this target:"); + writeln!(out, "Features supported by rustc for this target:"); for (feature, desc) in &rustc_target_features { - println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + writeln!(out, " {1:0$} - {2}.", max_feature_len, feature, desc); } - println!("\nCode-generation features supported by LLVM for this target:"); + writeln!(out, "\nCode-generation features supported by LLVM for this target:"); for (feature, desc) in &llvm_target_features { - println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + writeln!(out, " {1:0$} - {2}.", max_feature_len, feature, desc); } if llvm_target_features.is_empty() { - println!(" Target features listing is not supported by this LLVM version."); + writeln!(out, " Target features listing is not supported by this LLVM version."); } - println!("\nUse +feature to enable a feature, or -feature to disable it."); - println!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); - println!("Code-generation features cannot be used in cfg or #[target_feature],"); - println!("and may be renamed or removed in a future version of LLVM or rustc.\n"); + writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it."); + writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); + writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],"); + writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n"); } -pub(crate) fn print(req: PrintRequest, sess: &Session) { +pub(crate) fn print(req: PrintRequest, mut out: &mut dyn PrintBackendInfo, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); match req { @@ -410,9 +411,21 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) { // at least as long as the C function let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref())) .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e)); - unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) }; + unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) { + let out = &mut *(out as *mut &mut dyn PrintBackendInfo); + let bytes = slice::from_raw_parts(string as *const u8, len); + write!(out, "{}", String::from_utf8_lossy(bytes)); + } + unsafe { + llvm::LLVMRustPrintTargetCPUs( + tm, + cpu_cstring.as_ptr(), + callback, + &mut out as *mut &mut dyn PrintBackendInfo as *mut c_void, + ); + } } - PrintRequest::TargetFeatures => print_target_features(sess, tm), + PrintRequest::TargetFeatures => print_target_features(out, sess, tm), _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index b3c9ecf8b938b..f2f59ff0a3093 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -23,6 +23,8 @@ use rustc_span::symbol::Symbol; use rustc_target::abi::call::FnAbi; use rustc_target::spec::Target; +use std::fmt; + pub trait BackendTypes { type Value: CodegenObject; type Function: CodegenObject; @@ -61,7 +63,7 @@ pub trait CodegenBackend { fn locale_resource(&self) -> &'static str; fn init(&self, _sess: &Session) {} - fn print(&self, _req: PrintRequest, _sess: &Session) {} + fn print(&self, _req: PrintRequest, _out: &mut dyn PrintBackendInfo, _sess: &Session) {} fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec { vec![] } @@ -162,3 +164,19 @@ pub trait ExtraBackendMethods: std::thread::Builder::new().name(name).spawn(f) } } + +pub trait PrintBackendInfo { + fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>); +} + +impl PrintBackendInfo for String { + fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>) { + fmt::Write::write_fmt(self, args).unwrap(); + } +} + +impl dyn PrintBackendInfo + '_ { + pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) { + self.infallible_write_fmt(args); + } +} diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 8cb58bd4c704d..728c2bc8c49bc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -30,7 +30,9 @@ mod write; pub use self::abi::AbiBuilderMethods; pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; +pub use self::backend::{ + Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo, +}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; pub use self::coverageinfo::CoverageInfoBuilderMethods; diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 22b4ec6b0d1b1..77537ab591dee 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -16,4 +16,4 @@ driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file -driver_impl_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}` +driver_impl_unpretty_dump_fail = failed to write `{$path}` due to error `{$err}` diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 2e26ce111f01d..d9a81287f9ecc 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -51,6 +51,7 @@ use std::cmp::max; use std::collections::BTreeMap; use std::env; use std::ffi::OsString; +use std::fmt::Write as _; use std::fs; use std::io::{self, IsTerminal, Read, Write}; use std::panic::{self, catch_unwind}; @@ -67,6 +68,11 @@ macro do_not_use_print($($t:tt)*) { ) } +#[allow(unused_macros)] +macro do_not_use_safe_print($($t:tt)*) { + std::compile_error!("Don't use `safe_print` or `safe_println` here, use `println_info` instead") +} + // This import blocks the use of panicking `print` and `println` in all the code // below. Please use `safe_print` and `safe_println` to avoid ICE when // encountering an I/O error during print. @@ -718,6 +724,13 @@ fn print_crate_info( parse_attrs: bool, ) -> Compilation { use rustc_session::config::PrintRequest::*; + + // This import prevents the following code from using the printing macros + // used by the rest of the module. Within this function, we only write to + // the output specified by `sess.io.output_file`. + #[allow(unused_imports)] + use {do_not_use_safe_print as safe_print, do_not_use_safe_print as safe_println}; + // NativeStaticLibs and LinkArgs are special - printed during linking // (empty iterator returns true) if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) { @@ -736,17 +749,24 @@ fn print_crate_info( } else { None }; + + let mut crate_info = String::new(); + macro println_info($($arg:tt)*) { + crate_info.write_fmt(format_args!("{}\n", format_args!($($arg)*))).unwrap() + } + + let mut how_to_proceed = Compilation::Stop; for req in &sess.opts.prints { match *req { TargetList => { let mut targets = rustc_target::spec::TARGETS.to_vec(); targets.sort_unstable(); - safe_println!("{}", targets.join("\n")); + println_info!("{}", targets.join("\n")); } - Sysroot => safe_println!("{}", sess.sysroot.display()), - TargetLibdir => safe_println!("{}", sess.target_tlib_path.dir.display()), + Sysroot => println_info!("{}", sess.sysroot.display()), + TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()), TargetSpec => { - safe_println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); + println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } AllTargetSpecs => { let mut targets = BTreeMap::new(); @@ -755,24 +775,25 @@ fn print_crate_info( let target = Target::expect_builtin(&triple); targets.insert(name, target.to_json()); } - safe_println!("{}", serde_json::to_string_pretty(&targets).unwrap()); + println_info!("{}", serde_json::to_string_pretty(&targets).unwrap()); } FileNames | CrateName => { let Some(attrs) = attrs.as_ref() else { // no crate attributes, print out an error and exit - return Compilation::Continue; + how_to_proceed = Compilation::Continue; + break; }; let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let id = rustc_session::output::find_crate_name(sess, attrs); if *req == PrintRequest::CrateName { - safe_println!("{id}"); + println_info!("{id}"); continue; } let crate_types = collect_crate_types(sess, attrs); for &style in &crate_types { let fname = rustc_session::output::filename_for_input(sess, style, id, &t_outputs); - safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); + println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); } } Cfg => { @@ -806,13 +827,13 @@ fn print_crate_info( cfgs.sort(); for cfg in cfgs { - safe_println!("{cfg}"); + println_info!("{cfg}"); } } CallingConventions => { let mut calling_conventions = rustc_target::spec::abi::all_names(); calling_conventions.sort_unstable(); - safe_println!("{}", calling_conventions.join("\n")); + println_info!("{}", calling_conventions.join("\n")); } RelocationModels | CodeModels @@ -820,7 +841,7 @@ fn print_crate_info( | TargetCPUs | StackProtectorStrategies | TargetFeatures => { - codegen_backend.print(*req, sess); + codegen_backend.print(*req, &mut crate_info, sess); } // Any output here interferes with Cargo's parsing of other printed output NativeStaticLibs => {} @@ -830,7 +851,7 @@ fn print_crate_info( for split in &[Off, Packed, Unpacked] { if sess.target.options.supported_split_debuginfo.contains(split) { - safe_println!("{split}"); + println_info!("{split}"); } } } @@ -838,7 +859,7 @@ fn print_crate_info( use rustc_target::spec::current_apple_deployment_target; if sess.target.is_like_osx { - safe_println!( + println_info!( "deployment_target={}", current_apple_deployment_target(&sess.target) .expect("unknown Apple target OS") @@ -850,7 +871,9 @@ fn print_crate_info( } } } - Compilation::Stop + + pretty::write_or_print(&crate_info, sess); + how_to_proceed } /// Prints version information diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 24a5f4030b88d..273825b53ff8d 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -357,7 +357,7 @@ fn get_source(sess: &Session) -> (String, FileName) { (src, src_name) } -fn write_or_print(out: &str, sess: &Session) { +pub fn write_or_print(out: &str, sess: &Session) { match &sess.io.output_file { None | Some(OutFileName::Stdout) => print!("{out}"), Some(OutFileName::Real(p)) => { diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index eb3d67e720f2d..e5fb6b0953f5a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -306,44 +307,55 @@ static size_t getLongestEntryLength(ArrayRef Table) { return MaxLen; } -extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, const char* TargetCPU) { +using PrintBackendInfo = void(void*, const char* Data, size_t Len); + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, + const char* TargetCPU, + PrintBackendInfo Print, + void* Out) { const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch(); const Triple::ArchType TargetArch = Target->getTargetTriple().getArch(); + std::ostringstream Buf; + #if LLVM_VERSION_GE(17, 0) const ArrayRef CPUTable = MCInfo->getAllProcessorDescriptions(); #elif defined(LLVM_RUSTLLVM) const ArrayRef CPUTable = MCInfo->getCPUTable(); #else - printf("Full target CPU help is not supported by this LLVM version.\n\n"); + Buf << "Full target CPU help is not supported by this LLVM version.\n\n"; SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} }; const ArrayRef CPUTable = TargetCPUKV; #endif unsigned MaxCPULen = getLongestEntryLength(CPUTable); - printf("Available CPUs for this target:\n"); + Buf << "Available CPUs for this target:\n"; // Don't print the "native" entry when the user specifies --target with a // different arch since that could be wrong or misleading. if (HostArch == TargetArch) { MaxCPULen = std::max(MaxCPULen, (unsigned) std::strlen("native")); const StringRef HostCPU = sys::getHostCPUName(); - printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", - MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); + Buf << " " << std::left << std::setw(MaxCPULen) << "native" + << " - Select the CPU of the current host " + "(currently " << HostCPU.str() << ").\n"; } for (auto &CPU : CPUTable) { // Compare cpu against current target to label the default if (strcmp(CPU.Key, TargetCPU) == 0) { - printf(" %-*s - This is the default target CPU" - " for the current build target (currently %s).", - MaxCPULen, CPU.Key, Target->getTargetTriple().str().c_str()); + Buf << " " << std::left << std::setw(MaxCPULen) << CPU.Key + << " - This is the default target CPU for the current build target " + "(currently " << Target->getTargetTriple().str() << ")."; } else { - printf(" %-*s", MaxCPULen, CPU.Key); + Buf << " " << CPU.Key; } - printf("\n"); + Buf << "\n"; } + + const auto &BufString = Buf.str(); + Print(Out, BufString.data(), BufString.size()); } extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { diff --git a/tests/run-make/print-cfg/Makefile b/tests/run-make/print-cfg/Makefile index 126f5768c90f2..24b5427d70826 100644 --- a/tests/run-make/print-cfg/Makefile +++ b/tests/run-make/print-cfg/Makefile @@ -2,7 +2,7 @@ include ../tools.mk -all: default +all: default output_to_file $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) windows $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) x86_64 $(RUSTC) --target i686-pc-windows-msvc --print cfg | $(CGREP) msvc @@ -11,6 +11,27 @@ all: default $(RUSTC) --target arm-unknown-linux-gnueabihf --print cfg | $(CGREP) target_abi= $(RUSTC) --target arm-unknown-linux-gnueabihf --print cfg | $(CGREP) eabihf +output_to_file: + # Backend-independent, printed by rustc_driver_impl/src/lib.rs + -rm $(TMPDIR)/cfg.txt + $(RUSTC) --target x86_64-pc-windows-gnu --print=cfg -o $(TMPDIR)/cfg.txt + $(CGREP) windows < $(TMPDIR)/cfg.txt + + # Printed from CodegenBackend trait impl in rustc_codegen_llvm/src/lib.rs + -rm $(TMPDIR)/cfg.txt + $(RUSTC) --print=relocation-models -o $(TMPDIR)/cfg.txt + $(CGREP) dynamic-no-pic < $(TMPDIR)/cfg.txt + + # Printed by compiler/rustc_codegen_llvm/src/llvm_util.rs + -rm $(TMPDIR)/cfg.txt + $(RUSTC) --target wasm32-unknown-unknown --print=target-features -o $(TMPDIR)/cfg.txt + $(CGREP) reference-types < $(TMPDIR)/cfg.txt + + # Printed by C++ code in rustc_llvm/llvm-wrapper/PassWrapper.cpp + -rm $(TMPDIR)/cfg.txt + $(RUSTC) --target x86_64-pc-windows-gnu --print=target-cpus -o $(TMPDIR)/cfg.txt + $(CGREP) native < $(TMPDIR)/cfg.txt + ifdef IS_WINDOWS default: $(RUSTC) --print cfg | $(CGREP) windows diff --git a/tests/ui/codegen/target-cpus.rs b/tests/ui/codegen/target-cpus.rs new file mode 100644 index 0000000000000..1dff3ee6011bf --- /dev/null +++ b/tests/ui/codegen/target-cpus.rs @@ -0,0 +1,4 @@ +// needs-llvm-components: webassembly +// min-llvm-version: 17 +// compile-flags: --print=target-cpus --target=wasm32-unknown-unknown +// check-pass diff --git a/tests/ui/codegen/target-cpus.stdout b/tests/ui/codegen/target-cpus.stdout new file mode 100644 index 0000000000000..f60ba0f5034ba --- /dev/null +++ b/tests/ui/codegen/target-cpus.stdout @@ -0,0 +1,4 @@ +Available CPUs for this target: + bleeding-edge + generic - This is the default target CPU for the current build target (currently wasm32-unknown-unknown). + mvp diff --git a/tests/ui/unpretty/avoid-crash.stderr b/tests/ui/unpretty/avoid-crash.stderr index 11cd3866fa869..15bcc277e6491 100644 --- a/tests/ui/unpretty/avoid-crash.stderr +++ b/tests/ui/unpretty/avoid-crash.stderr @@ -1,4 +1,4 @@ -error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE +error: failed to write `/tmp/` due to $ERROR_MESSAGE error: aborting due to previous error