Auto merge of #117465 - paulmenage:small-data-limit, r=compiler-errors

Add -Z small-data-threshold

This flag allows specifying the threshold size above which LLVM should not consider placing small objects in a `.sdata` or `.sbss` section.

Support is indicated in the target options via the
small-data-threshold-support target option, which can indicate either an
LLVM argument or an LLVM module flag.  To avoid duplicate specifications
in a large number of targets, the default value for support is
DefaultForArch, which is translated to a concrete value according to the
target's architecture.
This commit is contained in:
bors 2024-09-12 04:27:08 +00:00
commit 1f51450c68
10 changed files with 241 additions and 4 deletions

View File

@ -27,7 +27,7 @@
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::call::FnAbi; use rustc_target::abi::call::FnAbi;
use rustc_target::abi::{HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_target::abi::{HasDataLayout, TargetDataLayout, VariantIdx};
use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::back::write::to_llvm_code_model; use crate::back::write::to_llvm_code_model;
@ -387,6 +387,24 @@ pub(crate) unsafe fn create_module<'ll>(
} }
} }
match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support())
{
// Set up the small-data optimization limit for architectures that use
// an LLVM module flag to control this.
(Some(threshold), SmallDataThresholdSupport::LlvmModuleFlag(flag)) => {
let flag = SmallCStr::new(flag.as_ref());
unsafe {
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Error,
flag.as_c_str().as_ptr(),
threshold as u32,
)
}
}
_ => (),
};
// Insert `llvm.ident` metadata. // Insert `llvm.ident` metadata.
// //
// On the wasm targets it will get hooked up to the "producer" sections // On the wasm targets it will get hooked up to the "producer" sections

View File

@ -14,7 +14,7 @@
use rustc_session::config::{PrintKind, PrintRequest}; use rustc_session::config::{PrintKind, PrintRequest};
use rustc_session::Session; use rustc_session::Session;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy}; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
use crate::back::write::create_informational_target_machine; use crate::back::write::create_informational_target_machine;
@ -125,6 +125,18 @@ fn llvm_arg_to_arg_name(full_arg: &str) -> &str {
for arg in sess_args { for arg in sess_args {
add(&(*arg), true); add(&(*arg), true);
} }
match (
sess.opts.unstable_opts.small_data_threshold,
sess.target.small_data_threshold_support(),
) {
// Set up the small-data optimization limit for architectures that use
// an LLVM argument to control this.
(Some(threshold), SmallDataThresholdSupport::LlvmArg(arg)) => {
add(&format!("--{arg}={threshold}"), false)
}
_ => (),
};
} }
if sess.opts.unstable_opts.llvm_time_trace { if sess.opts.unstable_opts.llvm_time_trace {

View File

@ -847,6 +847,7 @@ macro_rules! tracked {
tracked!(share_generics, Some(true)); tracked!(share_generics, Some(true));
tracked!(show_span, Some(String::from("abc"))); tracked!(show_span, Some(String::from("abc")));
tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc"))); tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
tracked!(small_data_threshold, Some(16));
tracked!(split_lto_unit, Some(true)); tracked!(split_lto_unit, Some(true));
tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
tracked!(stack_protector, StackProtector::All); tracked!(stack_protector, StackProtector::All);

View File

@ -113,6 +113,8 @@ session_split_lto_unit_requires_lto = `-Zsplit-lto-unit` requires `-Clto`, `-Clt
session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`
session_target_small_data_threshold_not_supported = `-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored
session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored
session_unleashed_feature_help_named = skipping check for `{$gate}` feature session_unleashed_feature_help_named = skipping check for `{$gate}` feature

View File

@ -182,6 +182,12 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> {
pub(crate) target_triple: &'a TargetTriple, pub(crate) target_triple: &'a TargetTriple,
} }
#[derive(Diagnostic)]
#[diag(session_target_small_data_threshold_not_supported)]
pub(crate) struct SmallDataThresholdNotSupportedForTarget<'a> {
pub(crate) target_triple: &'a TargetTriple,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(session_branch_protection_requires_aarch64)] #[diag(session_branch_protection_requires_aarch64)]
pub(crate) struct BranchProtectionRequiresAArch64; pub(crate) struct BranchProtectionRequiresAArch64;

View File

@ -2014,6 +2014,8 @@ pub(crate) fn parse_mir_include_spans(slot: &mut MirIncludeSpans, v: Option<&str
simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
"simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \
to rust's source base directory. only meant for testing purposes"), to rust's source base directory. only meant for testing purposes"),
small_data_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
"Set the threshold for objects to be stored in a \"small data\" section"),
span_debug: bool = (false, parse_bool, [UNTRACKED], span_debug: bool = (false, parse_bool, [UNTRACKED],
"forward proc_macro::Span's `Debug` impl to `Span`"), "forward proc_macro::Span's `Debug` impl to `Span`"),
/// o/w tests have closure@path /// o/w tests have closure@path

View File

@ -30,8 +30,8 @@
use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol}; use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol};
use rustc_target::asm::InlineAsmArch; use rustc_target::asm::InlineAsmArch;
use rustc_target::spec::{ use rustc_target::spec::{
CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
StackProtector, Target, TargetTriple, TlsModel, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel,
}; };
use crate::code_stats::CodeStats; use crate::code_stats::CodeStats;
@ -1278,6 +1278,14 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
} }
} }
if sess.opts.unstable_opts.small_data_threshold.is_some() {
if sess.target.small_data_threshold_support() == SmallDataThresholdSupport::None {
sess.dcx().emit_warn(errors::SmallDataThresholdNotSupportedForTarget {
target_triple: &sess.opts.target_triple,
})
}
}
if sess.opts.unstable_opts.branch_protection.is_some() && sess.target.arch != "aarch64" { if sess.opts.unstable_opts.branch_protection.is_some() && sess.target.arch != "aarch64" {
sess.dcx().emit_err(errors::BranchProtectionRequiresAArch64); sess.dcx().emit_err(errors::BranchProtectionRequiresAArch64);
} }

View File

@ -855,6 +855,43 @@ fn to_json(&self) -> Json {
} }
} }
#[derive(Clone, Debug, PartialEq, Hash)]
pub enum SmallDataThresholdSupport {
None,
DefaultForArch,
LlvmModuleFlag(StaticCow<str>),
LlvmArg(StaticCow<str>),
}
impl FromStr for SmallDataThresholdSupport {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "none" {
Ok(Self::None)
} else if s == "default-for-arch" {
Ok(Self::DefaultForArch)
} else if let Some(flag) = s.strip_prefix("llvm-module-flag=") {
Ok(Self::LlvmModuleFlag(flag.to_string().into()))
} else if let Some(arg) = s.strip_prefix("llvm-arg=") {
Ok(Self::LlvmArg(arg.to_string().into()))
} else {
Err(())
}
}
}
impl ToJson for SmallDataThresholdSupport {
fn to_json(&self) -> Value {
match self {
Self::None => "none".to_json(),
Self::DefaultForArch => "default-for-arch".to_json(),
Self::LlvmModuleFlag(flag) => format!("llvm-module-flag={flag}").to_json(),
Self::LlvmArg(arg) => format!("llvm-arg={arg}").to_json(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum MergeFunctions { pub enum MergeFunctions {
Disabled, Disabled,
@ -2392,6 +2429,9 @@ pub struct TargetOptions {
/// Whether the target supports XRay instrumentation. /// Whether the target supports XRay instrumentation.
pub supports_xray: bool, pub supports_xray: bool,
/// Whether the targets supports -Z small-data-threshold
small_data_threshold_support: SmallDataThresholdSupport,
} }
/// Add arguments for the given flavor and also for its "twin" flavors /// Add arguments for the given flavor and also for its "twin" flavors
@ -2609,6 +2649,7 @@ fn default() -> TargetOptions {
entry_name: "main".into(), entry_name: "main".into(),
entry_abi: Conv::C, entry_abi: Conv::C,
supports_xray: false, supports_xray: false,
small_data_threshold_support: SmallDataThresholdSupport::DefaultForArch,
} }
} }
} }
@ -2884,6 +2925,15 @@ macro_rules! key {
Some(Ok(())) Some(Ok(()))
})).unwrap_or(Ok(())) })).unwrap_or(Ok(()))
} ); } );
($key_name:ident, SmallDataThresholdSupport) => ( {
obj.remove("small-data-threshold-support").and_then(|o| o.as_str().and_then(|s| {
match s.parse::<SmallDataThresholdSupport>() {
Ok(support) => base.small_data_threshold_support = support,
_ => return Some(Err(format!("'{s}' is not a valid value for small-data-threshold-support."))),
}
Some(Ok(()))
})).unwrap_or(Ok(()))
} );
($key_name:ident, PanicStrategy) => ( { ($key_name:ident, PanicStrategy) => ( {
let name = (stringify!($key_name)).replace("_", "-"); let name = (stringify!($key_name)).replace("_", "-");
obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
@ -3321,6 +3371,7 @@ macro_rules! key {
key!(supported_sanitizers, SanitizerSet)?; key!(supported_sanitizers, SanitizerSet)?;
key!(generate_arange_section, bool); key!(generate_arange_section, bool);
key!(supports_stack_protector, bool); key!(supports_stack_protector, bool);
key!(small_data_threshold_support, SmallDataThresholdSupport)?;
key!(entry_name); key!(entry_name);
key!(entry_abi, Conv)?; key!(entry_abi, Conv)?;
key!(supports_xray, bool); key!(supports_xray, bool);
@ -3415,6 +3466,30 @@ fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> {
} }
} }
} }
/// Return the target's small data threshold support, converting
/// `DefaultForArch` into a concrete value.
pub fn small_data_threshold_support(&self) -> SmallDataThresholdSupport {
match &self.options.small_data_threshold_support {
// Avoid having to duplicate the small data support in every
// target file by supporting a default value for each
// architecture.
SmallDataThresholdSupport::DefaultForArch => match self.arch.as_ref() {
"mips" | "mips64" | "mips32r6" => {
SmallDataThresholdSupport::LlvmArg("mips-ssection-threshold".into())
}
"hexagon" => {
SmallDataThresholdSupport::LlvmArg("hexagon-small-data-threshold".into())
}
"m68k" => SmallDataThresholdSupport::LlvmArg("m68k-ssection-threshold".into()),
"riscv32" | "riscv64" => {
SmallDataThresholdSupport::LlvmModuleFlag("SmallDataLimit".into())
}
_ => SmallDataThresholdSupport::None,
},
s => s.clone(),
}
}
} }
impl ToJson for Target { impl ToJson for Target {
@ -3577,6 +3652,7 @@ macro_rules! target_option_val {
target_option_val!(c_enum_min_bits); target_option_val!(c_enum_min_bits);
target_option_val!(generate_arange_section); target_option_val!(generate_arange_section);
target_option_val!(supports_stack_protector); target_option_val!(supports_stack_protector);
target_option_val!(small_data_threshold_support);
target_option_val!(entry_name); target_option_val!(entry_name);
target_option_val!(entry_abi); target_option_val!(entry_abi);
target_option_val!(supports_xray); target_option_val!(supports_xray);

View File

@ -0,0 +1,20 @@
# `small-data-threshold`
-----------------------
This flag controls the maximum static variable size that may be included in the
"small data sections" (.sdata, .sbss) supported by some architectures (RISCV,
MIPS, M68K, Hexagon). Can be set to `0` to disable the use of small data
sections.
Target support is indicated by the `small_data_threshold_support` target
option which can be:
- `none` (`SmallDataThresholdSupport::None`) for no support
- `default-for-arch` (`SmallDataThresholdSupport::DefaultForArch`) which
is automatically translated into an appropriate value for the target.
- `llvm-module-flag=<flag_name>`
(`SmallDataThresholdSupport::LlvmModuleFlag`) for specifying the
threshold via an LLVM module flag
- `llvm-arg=<arg_name>` (`SmallDataThresholdSupport::LlvmArg`) for
specifying the threshold via an LLVM argument.

View File

@ -0,0 +1,92 @@
// Test for -Z small_data_threshold=...
//@ revisions: RISCV MIPS HEXAGON M68K
//@ assembly-output: emit-asm
//@ compile-flags: -Z small_data_threshold=4
//@ [RISCV] compile-flags: --target=riscv32im-unknown-none-elf
//@ [RISCV] needs-llvm-components: riscv
//@ [MIPS] compile-flags: --target=mips-unknown-linux-uclibc -C relocation-model=static
//@ [MIPS] compile-flags: -C llvm-args=-mgpopt -C llvm-args=-mlocal-sdata
//@ [MIPS] compile-flags: -C target-feature=+noabicalls
//@ [MIPS] needs-llvm-components: mips
//@ [HEXAGON] compile-flags: --target=hexagon-unknown-linux-musl -C target-feature=+small-data
//@ [HEXAGON] compile-flags: -C llvm-args=--hexagon-statics-in-small-data
//@ [HEXAGON] needs-llvm-components: hexagon
//@ [M68K] compile-flags: --target=m68k-unknown-linux-gnu
//@ [M68K] needs-llvm-components: m68k
#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#![crate_type = "lib"]
#[lang = "sized"]
trait Sized {}
#[lang = "drop_in_place"]
fn drop_in_place<T>(_: *mut T) {}
#[used]
#[no_mangle]
// U is below the threshold, should be in sdata
static mut U: u16 = 123;
#[used]
#[no_mangle]
// V is below the threshold, should be in sbss
static mut V: u16 = 0;
#[used]
#[no_mangle]
// W is at the threshold, should be in sdata
static mut W: u32 = 123;
#[used]
#[no_mangle]
// X is at the threshold, should be in sbss
static mut X: u32 = 0;
#[used]
#[no_mangle]
// Y is over the threshold, should be in its own .data section
static mut Y: u64 = 123;
#[used]
#[no_mangle]
// Z is over the threshold, should be in its own .bss section
static mut Z: u64 = 0;
// Currently, only MIPS and RISCV successfully put any objects in the small data
// sections so the U/V/W/X tests are skipped on Hexagon and M68K
//@ RISCV: .section .sdata,
//@ RISCV-NOT: .section
//@ RISCV: U:
//@ RISCV: .section .sbss
//@ RISCV-NOT: .section
//@ RISCV: V:
//@ RISCV: .section .sdata
//@ RISCV-NOT: .section
//@ RISCV: W:
//@ RISCV: .section .sbss
//@ RISCV-NOT: .section
//@ RISCV: X:
//@ MIPS: .section .sdata,
//@ MIPS-NOT: .section
//@ MIPS: U:
//@ MIPS: .section .sbss
//@ MIPS-NOT: .section
//@ MIPS: V:
//@ MIPS: .section .sdata
//@ MIPS-NOT: .section
//@ MIPS: W:
//@ MIPS: .section .sbss
//@ MIPS-NOT: .section
//@ MIPS: X:
//@ CHECK: .section .data.Y,
//@ CHECK-NOT: .section
//@ CHECK: Y:
//@ CHECK: .section .bss.Z,
//@ CHECK-NOT: .section
//@ CHECK: Z: