Skip to content

Commit 76d5107

Browse files
committed
Treat -Ctarget-cpu as a target-modifier when targeting AVR, AMDGCN and NVPTX
For AVR, AMDGCN, and NVPTX, crates built with different target CPU values are not generally link-compatible. Add a `requires_consistent_cpu` flag to the target spec and enable it for these targets. When the flag is set, treat `-Ctarget-cpu` as a target modifier and require all linked crates to agree on its value. Reject `-Ctarget-cpu=native` before codegen for targets that set `requires_consistent_cpu` to true. Also do not include `native` in the printed `target-cpus` list for such targets. Add tests covering: - which built-in targets set `requires-consistent-cpu` - cross-crate behavior with and without `requires-consistent-cpu` - that an omitted `-Ctarget-cpu` compares equal to an explicitly specified default CPU - rejection and printing behavior for `native` - precedence of repeated `-Ctarget-cpu` flags in metadata comparison and LLVM IR
1 parent d2b2602 commit 76d5107

33 files changed

Lines changed: 497 additions & 36 deletions

File tree

compiler/rustc_codegen_cranelift/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig, back};
4242
use rustc_log::tracing::info;
4343
use rustc_middle::dep_graph::WorkProductMap;
4444
use rustc_session::Session;
45-
use rustc_session::config::OutputFilenames;
45+
use rustc_session::config::{NATIVE_CPU, OutputFilenames};
4646
use rustc_span::{Symbol, sym};
4747
use rustc_target::spec::{Arch, CfgAbi, Env, Os};
4848

@@ -341,7 +341,7 @@ fn build_isa(sess: &Session, jit: bool) -> Arc<dyn TargetIsa + 'static> {
341341
let flags = settings::Flags::new(flags_builder);
342342

343343
let isa_builder = match sess.opts.cg.target_cpu.as_deref() {
344-
Some("native") => cranelift_native::builder_with_options(true).unwrap(),
344+
Some(NATIVE_CPU) => cranelift_native::builder_with_options(true).unwrap(),
345345
Some(value) => {
346346
let mut builder =
347347
cranelift_codegen::isa::lookup(target_triple.clone()).unwrap_or_else(|err| {

compiler/rustc_codegen_gcc/src/gcc_util.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use gccjit::Context;
33
use rustc_codegen_ssa::target_features;
44
use rustc_data_structures::smallvec::{SmallVec, smallvec};
55
use rustc_session::Session;
6+
use rustc_session::config::NATIVE_CPU;
67
use rustc_target::spec::Arch;
78

89
fn gcc_features_by_flags(sess: &Session, features: &mut Vec<String>) {
@@ -115,7 +116,7 @@ fn arch_to_gcc(name: &str) -> &str {
115116
}
116117

117118
fn handle_native(name: &str) -> &str {
118-
if name != "native" {
119+
if name != NATIVE_CPU {
119120
return arch_to_gcc(name);
120121
}
121122

compiler/rustc_codegen_llvm/src/llvm_util.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_data_structures::small_c_str::SmallCStr;
1414
use rustc_fs_util::path_to_c_string;
1515
use rustc_middle::bug;
1616
use rustc_session::Session;
17-
use rustc_session::config::{PrintKind, PrintRequest};
17+
use rustc_session::config::{NATIVE_CPU, PrintKind, PrintRequest};
1818
use rustc_target::spec::{
1919
Arch, CfgAbi, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport,
2020
};
@@ -514,10 +514,12 @@ fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String)
514514

515515
// Only print the "native" entry when host and target are the same arch,
516516
// since otherwise it could be wrong or misleading.
517-
if sess.host.arch == sess.target.arch {
517+
// Also do not print it if `requires_consistent_cpu` is set, because in this case
518+
// "native" would be rejected.
519+
if sess.host.arch == sess.target.arch && !sess.target.requires_consistent_cpu {
518520
let host = get_host_cpu_name();
519521
cpus.push_front(Cpu {
520-
cpu_name: "native",
522+
cpu_name: NATIVE_CPU,
521523
remark: format!(" - Select the CPU of the current host (currently {host})."),
522524
});
523525
}
@@ -612,7 +614,7 @@ fn get_host_cpu_name() -> &'static str {
612614
/// LLVM. Otherwise, the string is returned as-is.
613615
fn handle_native(cpu_name: &str) -> &str {
614616
match cpu_name {
615-
"native" => get_host_cpu_name(),
617+
NATIVE_CPU => get_host_cpu_name(),
616618
_ => cpu_name,
617619
}
618620
}
@@ -666,7 +668,7 @@ pub(crate) fn global_llvm_features(sess: &Session, only_base_features: bool) ->
666668

667669
// -Ctarget-cpu=native
668670
match sess.opts.cg.target_cpu {
669-
Some(ref s) if s == "native" => {
671+
Some(ref s) if s == NATIVE_CPU => {
670672
// We have already figured out the actual CPU name with `LLVMRustGetHostCPUName` and set
671673
// that for LLVM, so the features implied by that CPU name will be available everywhere.
672674
// However, that is not sufficient: e.g. `skylake` alone is not sufficient to tell if

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ impl MetadataBlob {
749749
"lang_items".to_owned(),
750750
"features".to_owned(),
751751
"items".to_owned(),
752+
"target_modifiers".to_owned(),
752753
];
753754
let ls_kinds = if ls_kinds.contains(&"all".to_owned()) { &all_ls_kinds } else { ls_kinds };
754755

@@ -918,11 +919,28 @@ impl MetadataBlob {
918919

919920
write!(out, "\n")?;
920921
}
922+
"target_modifiers" => {
923+
writeln!(out, "=Target modifiers=")?;
924+
925+
for modifier in root.decode_target_modifiers(self) {
926+
let extended = modifier.extend();
927+
928+
writeln!(
929+
out,
930+
"-{}{}={} [{}]",
931+
extended.prefix,
932+
extended.name,
933+
modifier.value_name,
934+
extended.tech_value,
935+
)?;
936+
}
937+
}
921938

922939
_ => {
923940
writeln!(
924941
out,
925-
"unknown -Zls kind. allowed values are: all, root, lang_items, features, items"
942+
"unknown -Zls kind. allowed values are: all, root, lang_items, features, items, \
943+
target_modifiers"
926944
)?;
927945
}
928946
}

compiler/rustc_session/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ mod native_libs;
4646
mod print_request;
4747
pub mod sigpipe;
4848

49+
/// Special CPU name requesting the CPU of the current host.
50+
pub const NATIVE_CPU: &str = "native";
51+
4952
/// The different settings that the `-C strip` flag can have.
5053
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
5154
pub enum Strip {

compiler/rustc_session/src/diagnostics.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,3 +716,17 @@ pub(crate) struct ThinLtoNotSupportedByBackend;
716716
#[derive(Diagnostic)]
717717
#[diag("`-Zpacked-stack` is only supported on s390x")]
718718
pub(crate) struct UnsupportedPackedStack;
719+
720+
#[derive(Diagnostic)]
721+
#[diag("`-Ctarget-cpu=native` is not allowed for target `{$target_triple}`")]
722+
#[note("this target requires consistent `-Ctarget-cpu` values across all crates")]
723+
#[help(
724+
"specify the target CPU explicitly {$need_explicit_cpu ->
725+
[false] or leave it blank to use the default
726+
*[other] {\"\"}
727+
}"
728+
)]
729+
pub(crate) struct NativeTargetCpuNotAllowed<'a> {
730+
pub(crate) target_triple: &'a TargetTuple,
731+
pub(crate) need_explicit_cpu: bool,
732+
}

compiler/rustc_session/src/options.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,28 @@ mod target_modifier_consistency_check {
130130
}
131131
true
132132
}
133+
pub(super) fn target_cpu(
134+
sess: &Session,
135+
l: &TargetModifier,
136+
r: Option<&TargetModifier>,
137+
) -> bool {
138+
if !sess.target.requires_consistent_cpu {
139+
return true;
140+
}
141+
let l_tech_value = l.extend().tech_value;
142+
let r_tech_value = match r {
143+
Some(r) => r.extend().tech_value,
144+
// If only one of the two compared crates specifies the CPU
145+
// explicitly we compare against the target's default CPU.
146+
None => {
147+
// We reuse the same parsing logic.
148+
CodegenOptionsTargetModifiers::TargetCpu
149+
.reparse(sess.target.cpu.as_ref())
150+
.tech_value
151+
}
152+
};
153+
l_tech_value == r_tech_value
154+
}
133155
}
134156

135157
impl TargetModifier {
@@ -152,7 +174,11 @@ impl TargetModifier {
152174
}
153175
_ => {}
154176
},
155-
_ => {}
177+
OptionsTargetModifiers::CodegenOptions(codegen) => match codegen {
178+
CodegenOptionsTargetModifiers::TargetCpu => {
179+
return target_modifier_consistency_check::target_cpu(sess, self, other);
180+
}
181+
},
156182
};
157183
match other {
158184
Some(other) => self.extend().tech_value == other.extend().tech_value,
@@ -2273,7 +2299,7 @@ options! {
22732299
symbol_mangling_version: Option<SymbolManglingVersion> = (None,
22742300
parse_symbol_mangling_version, [TRACKED],
22752301
"which mangling version to use for symbol names ('legacy', 'v0' (default), or 'hashed')"),
2276-
target_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
2302+
target_cpu: Option<String> = (None, parse_opt_string, [TRACKED] { TARGET_MODIFIER: TargetCpu },
22772303
"select target processor (`rustc --print target-cpus` for details)"),
22782304
target_feature: String = (String::new(), parse_target_feature, [TRACKED],
22792305
"target specific attributes. (`rustc --print target-features` for details). \

compiler/rustc_session/src/session.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ use crate::code_stats::CodeStats;
3838
pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
3939
use crate::config::{
4040
self, Cfg, CheckCfg, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType,
41-
FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, OptLevel, OutFileName, OutputType,
42-
PointerAuthOption, SwitchWithOptPath,
41+
FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, NATIVE_CPU, OptLevel, OutFileName,
42+
OutputType, PointerAuthOption, SwitchWithOptPath,
4343
};
4444
use crate::filesearch::FileSearch;
4545
use crate::lint::LintId;
@@ -1695,6 +1695,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
16951695
sess.dcx().emit_err(diagnostics::UnsupportedPackedStack);
16961696
}
16971697
}
1698+
1699+
if let Some(ref cpu_name) = sess.opts.cg.target_cpu {
1700+
if cpu_name == NATIVE_CPU && sess.target.requires_consistent_cpu {
1701+
sess.dcx().emit_fatal(diagnostics::NativeTargetCpuNotAllowed {
1702+
target_triple: &sess.opts.target_triple,
1703+
need_explicit_cpu: sess.target.need_explicit_cpu,
1704+
});
1705+
}
1706+
}
16981707
}
16991708

17001709
/// Holds data on the current incremental compilation session, if there is one.

compiler/rustc_target/src/spec/json.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ impl Target {
116116
forward!(asm_args);
117117
forward!(cpu);
118118
forward!(need_explicit_cpu);
119+
forward!(requires_consistent_cpu);
119120
forward!(unsupported_cpus);
120121
forward!(features);
121122
forward!(dynamic_linking);
@@ -321,6 +322,7 @@ impl ToJson for Target {
321322
target_option_val!(asm_args);
322323
target_option_val!(cpu);
323324
target_option_val!(need_explicit_cpu);
325+
target_option_val!(requires_consistent_cpu);
324326
target_option_val!(unsupported_cpus);
325327
target_option_val!(features);
326328
target_option_val!(dynamic_linking);
@@ -543,6 +545,7 @@ struct TargetSpecJson {
543545
asm_args: Option<StaticCow<[StaticCow<str>]>>,
544546
cpu: Option<StaticCow<str>>,
545547
need_explicit_cpu: Option<bool>,
548+
requires_consistent_cpu: Option<bool>,
546549
unsupported_cpus: Option<StaticCow<[StaticCow<str>]>>,
547550
features: Option<StaticCow<str>>,
548551
dynamic_linking: Option<bool>,

compiler/rustc_target/src/spec/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore-tidy-filelength
12
//! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131)
23
//!
34
//! Rust targets a wide variety of usecases, and in the interest of flexibility,
@@ -2403,6 +2404,10 @@ pub struct TargetOptions {
24032404
/// Whether a cpu needs to be explicitly set.
24042405
/// Set to true if there is no default cpu. Defaults to false.
24052406
pub need_explicit_cpu: bool,
2407+
/// Whether `-Ctarget-cpu` is treated as a target modifier. If this is set
2408+
/// all crates that are linked together must have been compiled with the
2409+
/// same target-cpu. Defaults to false.
2410+
pub requires_consistent_cpu: bool,
24062411
/// A list of CPUs that are provided by LLVM but are considered unsupported by Rust.
24072412
/// These CPUs are omitted from `--print target-cpus` output and will cause an error
24082413
/// if used with `-Ctarget-cpu`.
@@ -2860,6 +2865,7 @@ impl Default for TargetOptions {
28602865
asm_args: cvs![],
28612866
cpu: "generic".into(),
28622867
need_explicit_cpu: false,
2868+
requires_consistent_cpu: false,
28632869
unsupported_cpus: cvs![],
28642870
features: "".into(),
28652871
direct_access_external_data: None,
@@ -3636,6 +3642,14 @@ impl Target {
36363642
}
36373643
}
36383644

3645+
// Check that the target cpu constraints make sense.
3646+
if self.need_explicit_cpu {
3647+
check!(
3648+
self.requires_consistent_cpu,
3649+
"if `need_explicit_cpu` is set, then `requires_consistent_cpu` must be set"
3650+
);
3651+
}
3652+
36393653
// Check that the given target-features string makes some basic sense.
36403654
if !self.features.is_empty() {
36413655
let mut features_enabled = FxHashSet::default();

0 commit comments

Comments
 (0)