Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -Z small-data-threshold #117465

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:
Loading