Skip to content

Commit

Permalink
Add -Z small-data-threshold
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
paulmenage committed Sep 10, 2024
1 parent 33855f8 commit 3810386
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 4 deletions.
20 changes: 19 additions & 1 deletion compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_session/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
76 changes: 76 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
}
}
Expand Down Expand Up @@ -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| {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/small-data-threshold.md
Original file line number Diff line number Diff line change
@@ -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 changes: 92 additions & 0 deletions tests/assembly/small_data_threshold.rs
Original file line number Diff line number Diff line change
@@ -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:

0 comments on commit 3810386

Please sign in to comment.