From 2013ccc218a488768ad6b647dd8ecc293e7847b4 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 19 Apr 2023 18:53:23 +0300 Subject: [PATCH 1/4] rustc_target: Refactor linker flavor inference Go through an intermediate pair of `cc`and `lld` hints instead of mapping CLI options to `LinkerFlavor` directly, and use the target's default linker flavor as a reference. --- compiler/rustc_codegen_ssa/src/back/link.rs | 43 +--------- compiler/rustc_target/src/spec/mod.rs | 89 +++++++++++++++++---- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index bdbd10f826061..f8bb9bf2bb50b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -23,7 +23,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; -use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy}; +use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; @@ -1302,44 +1302,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| { sess.emit_fatal(errors::LinkerFileStem); }); - - // Remove any version postfix. - let stem = stem - .rsplit_once('-') - .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs)) - .unwrap_or(stem); - - // GCC/Clang can have an optional target prefix. - let flavor = if stem == "emcc" { - LinkerFlavor::EmCc - } else if stem == "gcc" - || stem.ends_with("-gcc") - || stem == "g++" - || stem.ends_with("-g++") - || stem == "clang" - || stem.ends_with("-clang") - || stem == "clang++" - || stem.ends_with("-clang++") - { - LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target) - } else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") { - LinkerFlavor::WasmLld(Cc::No) - } else if stem == "ld" || stem.ends_with("-ld") { - LinkerFlavor::from_cli(LinkerFlavorCli::Ld, &sess.target) - } else if stem == "ld.lld" { - LinkerFlavor::Gnu(Cc::No, Lld::Yes) - } else if stem == "link" { - LinkerFlavor::Msvc(Lld::No) - } else if stem == "lld-link" { - LinkerFlavor::Msvc(Lld::Yes) - } else if stem == "lld" || stem == "rust-lld" { - let lld_flavor = sess.target.linker_flavor.lld_flavor(); - LinkerFlavor::from_cli(LinkerFlavorCli::Lld(lld_flavor), &sess.target) - } else { - // fall back to the value in the target spec - sess.target.linker_flavor - }; - + let flavor = sess.target.linker_flavor.with_linker_hints(stem); Some((linker, flavor)) } (None, None) => None, @@ -1349,7 +1312,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { // linker and linker flavor specified via command line have precedence over what the target // specification specifies let linker_flavor = - sess.opts.cg.linker_flavor.map(|flavor| LinkerFlavor::from_cli(flavor, &sess.target)); + sess.opts.cg.linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)); if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) { return ret; } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 62f94209cf04d..6a3eea1427ffc 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -205,15 +205,11 @@ impl ToJson for LldFlavor { } impl LinkerFlavor { - pub fn from_cli(cli: LinkerFlavorCli, target: &TargetOptions) -> LinkerFlavor { - Self::from_cli_impl(cli, target.linker_flavor.lld_flavor(), target.linker_flavor.is_gnu()) - } - - /// The passed CLI flavor is preferred over other args coming from the default target spec, - /// so this function can produce a flavor that is incompatible with the current target. - /// FIXME: Produce errors when `-Clinker-flavor` is set to something incompatible - /// with the current target. - fn from_cli_impl(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor { + /// At this point the target's reference linker flavor doesn't yet exist and we need to infer + /// it. The inference always succeds and gives some result, and we don't report any flavor + /// incompatibility errors for json target specs. The CLI flavor is used as the main source + /// of truth, other flags are used in case of ambiguities. + fn from_cli_json(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor { match cli { LinkerFlavorCli::Gcc => match lld_flavor { LldFlavor::Ld if is_gnu => LinkerFlavor::Gnu(Cc::Yes, Lld::No), @@ -257,6 +253,72 @@ impl LinkerFlavor { } } + fn infer_cli_hints(cli: LinkerFlavorCli) -> (Option, Option) { + match cli { + LinkerFlavorCli::Gcc | LinkerFlavorCli::Em => (Some(Cc::Yes), None), + LinkerFlavorCli::Lld(_) => (Some(Cc::No), Some(Lld::Yes)), + LinkerFlavorCli::Ld | LinkerFlavorCli::Msvc => (Some(Cc::No), Some(Lld::No)), + LinkerFlavorCli::BpfLinker | LinkerFlavorCli::PtxLinker => (None, None), + } + } + + fn infer_linker_hints(linker_stem: &str) -> (Option, Option) { + // Remove any version postfix. + let stem = linker_stem + .rsplit_once('-') + .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs)) + .unwrap_or(linker_stem); + + // GCC/Clang can have an optional target prefix. + if stem == "emcc" + || stem == "gcc" + || stem.ends_with("-gcc") + || stem == "g++" + || stem.ends_with("-g++") + || stem == "clang" + || stem.ends_with("-clang") + || stem == "clang++" + || stem.ends_with("-clang++") + { + (Some(Cc::Yes), None) + } else if stem == "wasm-ld" + || stem.ends_with("-wasm-ld") + || stem == "ld.lld" + || stem == "lld" + || stem == "rust-lld" + || stem == "lld-link" + { + (Some(Cc::No), Some(Lld::Yes)) + } else if stem == "ld" || stem.ends_with("-ld") || stem == "link" { + (Some(Cc::No), Some(Lld::No)) + } else { + (None, None) + } + } + + fn with_hints(self, (cc_hint, lld_hint): (Option, Option)) -> LinkerFlavor { + match self { + LinkerFlavor::Gnu(cc, lld) => { + LinkerFlavor::Gnu(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld)) + } + LinkerFlavor::Darwin(cc, lld) => { + LinkerFlavor::Darwin(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld)) + } + LinkerFlavor::WasmLld(cc) => LinkerFlavor::WasmLld(cc_hint.unwrap_or(cc)), + LinkerFlavor::Unix(cc) => LinkerFlavor::Unix(cc_hint.unwrap_or(cc)), + LinkerFlavor::Msvc(lld) => LinkerFlavor::Msvc(lld_hint.unwrap_or(lld)), + LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => self, + } + } + + pub fn with_cli_hints(self, cli: LinkerFlavorCli) -> LinkerFlavor { + self.with_hints(LinkerFlavor::infer_cli_hints(cli)) + } + + pub fn with_linker_hints(self, linker_stem: &str) -> LinkerFlavor { + self.with_hints(LinkerFlavor::infer_linker_hints(linker_stem)) + } + pub fn lld_flavor(self) -> LldFlavor { match self { LinkerFlavor::Gnu(..) @@ -1801,7 +1863,7 @@ impl TargetOptions { } fn update_from_cli(&mut self) { - self.linker_flavor = LinkerFlavor::from_cli_impl( + self.linker_flavor = LinkerFlavor::from_cli_json( self.linker_flavor_json, self.lld_flavor_json, self.linker_is_gnu_json, @@ -1815,12 +1877,7 @@ impl TargetOptions { ] { args.clear(); for (flavor, args_json) in args_json { - // Cannot use `from_cli` due to borrow checker. - let linker_flavor = LinkerFlavor::from_cli_impl( - *flavor, - self.lld_flavor_json, - self.linker_is_gnu_json, - ); + let linker_flavor = self.linker_flavor.with_cli_hints(*flavor); // Normalize to no lld to avoid asserts. let linker_flavor = match linker_flavor { LinkerFlavor::Gnu(cc, _) => LinkerFlavor::Gnu(cc, Lld::No), From b0ce4164f09be1221d004c4b84e49bdcbf973194 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 25 Apr 2023 15:48:53 +0300 Subject: [PATCH 2/4] linker: Report linker flavors incompatible with the current target Previously they would be reported as link time errors about unknown linker options --- compiler/rustc_session/messages.ftl | 4 ++++ compiler/rustc_session/src/errors.rs | 8 +++++++ compiler/rustc_session/src/session.rs | 7 +++++++ compiler/rustc_target/src/lib.rs | 1 + compiler/rustc_target/src/spec/mod.rs | 21 +++++++++++++++++-- tests/ui/linkage-attr/incompatible-flavor.rs | 6 ++++++ .../linkage-attr/incompatible-flavor.stderr | 6 ++++++ 7 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/ui/linkage-attr/incompatible-flavor.rs create mode 100644 tests/ui/linkage-attr/incompatible-flavor.stderr diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 5a0b8f9f73cba..4897bd8d5daec 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -27,6 +27,10 @@ session_feature_gate_error = {$explain} session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported + +session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target + .note = compatible flavors are: {$compatible_list} + session_incorrect_cgu_reuse_type = CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least -> [one] {"at least "} diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 546c0fa8e03e3..4a3e668da111a 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -422,3 +422,11 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: pub struct OptimisationFuelExhausted { pub msg: String, } + +#[derive(Diagnostic)] +#[diag(session_incompatible_linker_flavor)] +#[note] +pub struct IncompatibleLinkerFlavor { + pub flavor: &'static str, + pub compatible_list: String, +} diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index bbe52dbced071..1eb54cee5a14c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1675,6 +1675,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) { if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { sess.emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); } + + if let Some(flavor) = sess.opts.cg.linker_flavor { + if let Some(compatible_list) = sess.target.linker_flavor.check_compatibility(flavor) { + let flavor = flavor.desc(); + sess.emit_err(errors::IncompatibleLinkerFlavor { flavor, compatible_list }); + } + } } /// Holds data on the current incremental compilation session, if there is one. diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index dc2cc23ffb1e4..a7b54766bc623 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -11,6 +11,7 @@ #![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] +#![feature(iter_intersperse)] #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 6a3eea1427ffc..05cb7e87a9365 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -319,6 +319,19 @@ impl LinkerFlavor { self.with_hints(LinkerFlavor::infer_linker_hints(linker_stem)) } + pub fn check_compatibility(self, cli: LinkerFlavorCli) -> Option { + // The CLI flavor should be compatible with the target if it survives this roundtrip. + let compatible = |cli| cli == self.with_cli_hints(cli).to_cli(); + (!compatible(cli)).then(|| { + LinkerFlavorCli::all() + .iter() + .filter(|cli| compatible(**cli)) + .map(|cli| cli.desc()) + .intersperse(", ") + .collect() + }) + } + pub fn lld_flavor(self) -> LldFlavor { match self { LinkerFlavor::Gnu(..) @@ -340,6 +353,10 @@ impl LinkerFlavor { macro_rules! linker_flavor_cli_impls { ($(($($flavor:tt)*) $string:literal)*) => ( impl LinkerFlavorCli { + const fn all() -> &'static [LinkerFlavorCli] { + &[$($($flavor)*,)*] + } + pub const fn one_of() -> &'static str { concat!("one of: ", $($string, " ",)*) } @@ -351,8 +368,8 @@ macro_rules! linker_flavor_cli_impls { }) } - pub fn desc(&self) -> &str { - match *self { + pub fn desc(self) -> &'static str { + match self { $($($flavor)* => $string,)* } } diff --git a/tests/ui/linkage-attr/incompatible-flavor.rs b/tests/ui/linkage-attr/incompatible-flavor.rs new file mode 100644 index 0000000000000..90c2b612f22da --- /dev/null +++ b/tests/ui/linkage-attr/incompatible-flavor.rs @@ -0,0 +1,6 @@ +// compile-flags: --target=x86_64-unknown-linux-gnu -C linker-flavor=msvc --crate-type=rlib +// error-pattern: linker flavor `msvc` is incompatible with the current target +// needs-llvm-components: + +#![feature(no_core)] +#![no_core] diff --git a/tests/ui/linkage-attr/incompatible-flavor.stderr b/tests/ui/linkage-attr/incompatible-flavor.stderr new file mode 100644 index 0000000000000..e07e778521c0b --- /dev/null +++ b/tests/ui/linkage-attr/incompatible-flavor.stderr @@ -0,0 +1,6 @@ +error: linker flavor `msvc` is incompatible with the current target + | + = note: compatible flavors are: gcc, ld, ld.lld + +error: aborting due to previous error + From 23f177d86d05c8076c692e1c07e325b2161ae893 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 23 May 2023 00:00:35 +0300 Subject: [PATCH 3/4] rustc_session: Feature gate linker flavors for tier 3 targets --- compiler/rustc_session/src/config.rs | 15 ++++++++++++++- tests/ui/linkage-attr/unstable-flavor.bpf.stderr | 2 ++ tests/ui/linkage-attr/unstable-flavor.ptx.stderr | 2 ++ tests/ui/linkage-attr/unstable-flavor.rs | 10 ++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/ui/linkage-attr/unstable-flavor.bpf.stderr create mode 100644 tests/ui/linkage-attr/unstable-flavor.ptx.stderr create mode 100644 tests/ui/linkage-attr/unstable-flavor.rs diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 6c8c8e484f939..0ce83e7909771 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -12,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; use rustc_target::abi::Align; -use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo}; +use rustc_target::spec::{LinkerFlavorCli, PanicStrategy, SanitizerSet, SplitDebuginfo}; use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS}; use crate::parse::{CrateCheckConfig, CrateConfig}; @@ -2525,6 +2525,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } } + if let Some(flavor) = cg.linker_flavor { + if matches!(flavor, LinkerFlavorCli::BpfLinker | LinkerFlavorCli::PtxLinker) + && !nightly_options::is_unstable_enabled(matches) + { + let msg = format!( + "linker flavor `{}` is unstable, `-Z unstable-options` \ + flag must also be passed to explicitly use it", + flavor.desc() + ); + early_error(error_format, msg); + } + } + let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format); let cg = cg; diff --git a/tests/ui/linkage-attr/unstable-flavor.bpf.stderr b/tests/ui/linkage-attr/unstable-flavor.bpf.stderr new file mode 100644 index 0000000000000..3346d12c20e80 --- /dev/null +++ b/tests/ui/linkage-attr/unstable-flavor.bpf.stderr @@ -0,0 +1,2 @@ +error: linker flavor `bpf-linker` is unstable, `-Z unstable-options` flag must also be passed to explicitly use it + diff --git a/tests/ui/linkage-attr/unstable-flavor.ptx.stderr b/tests/ui/linkage-attr/unstable-flavor.ptx.stderr new file mode 100644 index 0000000000000..03ca2a012460c --- /dev/null +++ b/tests/ui/linkage-attr/unstable-flavor.ptx.stderr @@ -0,0 +1,2 @@ +error: linker flavor `ptx-linker` is unstable, `-Z unstable-options` flag must also be passed to explicitly use it + diff --git a/tests/ui/linkage-attr/unstable-flavor.rs b/tests/ui/linkage-attr/unstable-flavor.rs new file mode 100644 index 0000000000000..5487882dc24dc --- /dev/null +++ b/tests/ui/linkage-attr/unstable-flavor.rs @@ -0,0 +1,10 @@ +// revisions: bpf ptx +// [bpf] compile-flags: --target=bpfel-unknown-none -C linker-flavor=bpf-linker --crate-type=rlib +// [bpf] error-pattern: linker flavor `bpf-linker` is unstable, `-Z unstable-options` flag +// [bpf] needs-llvm-components: +// [ptx] compile-flags: --target=nvptx64-nvidia-cuda -C linker-flavor=ptx-linker --crate-type=rlib +// [ptx] error-pattern: linker flavor `ptx-linker` is unstable, `-Z unstable-options` flag +// [ptx] needs-llvm-components: + +#![feature(no_core)] +#![no_core] From 2f7328d970b1ee63d8b62646561923f7f137842e Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 29 May 2023 19:58:01 +0300 Subject: [PATCH 4/4] Fix tests on non-unix targets --- tests/ui/linkage-attr/issue-10755.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/linkage-attr/issue-10755.rs b/tests/ui/linkage-attr/issue-10755.rs index afd2dc46ca3cb..0df5d842cb2a0 100644 --- a/tests/ui/linkage-attr/issue-10755.rs +++ b/tests/ui/linkage-attr/issue-10755.rs @@ -1,6 +1,6 @@ // build-fail // dont-check-compiler-stderr -// compile-flags: -C linker=llllll -C linker-flavor=ld +// compile-flags: -C linker=llllll // error-pattern: `llllll` // Before, the error-pattern checked for "not found". On WSL with appendWindowsPath=true, running