mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
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:
parent
33855f80d4
commit
3810386bbe
@ -27,7 +27,7 @@ use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
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 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.
|
||||
//
|
||||
// On the wasm targets it will get hooked up to the "producer" sections
|
||||
|
@ -14,7 +14,7 @@ use rustc_middle::bug;
|
||||
use rustc_session::config::{PrintKind, PrintRequest};
|
||||
use rustc_session::Session;
|
||||
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 crate::back::write::create_informational_target_machine;
|
||||
@ -125,6 +125,18 @@ unsafe fn configure_llvm(sess: &Session) {
|
||||
for arg in sess_args {
|
||||
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 {
|
||||
|
@ -847,6 +847,7 @@ fn test_unstable_options_tracking_hash() {
|
||||
tracked!(share_generics, Some(true));
|
||||
tracked!(show_span, Some(String::from("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!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
|
||||
tracked!(stack_protector, StackProtector::All);
|
||||
|
@ -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_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_unleashed_feature_help_named = skipping check for `{$gate}` feature
|
||||
|
@ -182,6 +182,12 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> {
|
||||
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)]
|
||||
#[diag(session_branch_protection_requires_aarch64)]
|
||||
pub(crate) struct BranchProtectionRequiresAArch64;
|
||||
|
@ -2014,6 +2014,8 @@ written to standard error output)"),
|
||||
simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
|
||||
"simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \
|
||||
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],
|
||||
"forward proc_macro::Span's `Debug` impl to `Span`"),
|
||||
/// o/w tests have closure@path
|
||||
|
@ -30,8 +30,8 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol};
|
||||
use rustc_target::asm::InlineAsmArch;
|
||||
use rustc_target::spec::{
|
||||
CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo,
|
||||
StackProtector, Target, TargetTriple, TlsModel,
|
||||
CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
|
||||
SmallDataThresholdSupport, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel,
|
||||
};
|
||||
|
||||
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" {
|
||||
sess.dcx().emit_err(errors::BranchProtectionRequiresAArch64);
|
||||
}
|
||||
|
@ -855,6 +855,43 @@ impl ToJson for RelroLevel {
|
||||
}
|
||||
}
|
||||
|
||||
#[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)]
|
||||
pub enum MergeFunctions {
|
||||
Disabled,
|
||||
@ -2392,6 +2429,9 @@ pub struct TargetOptions {
|
||||
|
||||
/// Whether the target supports XRay instrumentation.
|
||||
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
|
||||
@ -2609,6 +2649,7 @@ impl Default for TargetOptions {
|
||||
entry_name: "main".into(),
|
||||
entry_abi: Conv::C,
|
||||
supports_xray: false,
|
||||
small_data_threshold_support: SmallDataThresholdSupport::DefaultForArch,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2884,6 +2925,15 @@ impl Target {
|
||||
Some(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) => ( {
|
||||
let name = (stringify!($key_name)).replace("_", "-");
|
||||
obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
|
||||
@ -3321,6 +3371,7 @@ impl Target {
|
||||
key!(supported_sanitizers, SanitizerSet)?;
|
||||
key!(generate_arange_section, bool);
|
||||
key!(supports_stack_protector, bool);
|
||||
key!(small_data_threshold_support, SmallDataThresholdSupport)?;
|
||||
key!(entry_name);
|
||||
key!(entry_abi, Conv)?;
|
||||
key!(supports_xray, bool);
|
||||
@ -3415,6 +3466,30 @@ impl Target {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
@ -3577,6 +3652,7 @@ impl ToJson for Target {
|
||||
target_option_val!(c_enum_min_bits);
|
||||
target_option_val!(generate_arange_section);
|
||||
target_option_val!(supports_stack_protector);
|
||||
target_option_val!(small_data_threshold_support);
|
||||
target_option_val!(entry_name);
|
||||
target_option_val!(entry_abi);
|
||||
target_option_val!(supports_xray);
|
||||
|
@ -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.
|
92
tests/assembly/small_data_threshold.rs
Normal file
92
tests/assembly/small_data_threshold.rs
Normal 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:
|
Loading…
Reference in New Issue
Block a user