Skip to content

Commit 3810386

Browse files
committed
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.
1 parent 33855f8 commit 3810386

File tree

10 files changed

+241
-4
lines changed

10 files changed

+241
-4
lines changed

Diff for: compiler/rustc_codegen_llvm/src/context.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rustc_span::source_map::Spanned;
2727
use rustc_span::{Span, DUMMY_SP};
2828
use rustc_target::abi::call::FnAbi;
2929
use rustc_target::abi::{HasDataLayout, TargetDataLayout, VariantIdx};
30-
use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel};
30+
use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel};
3131
use smallvec::SmallVec;
3232

3333
use crate::back::write::to_llvm_code_model;
@@ -387,6 +387,24 @@ pub(crate) unsafe fn create_module<'ll>(
387387
}
388388
}
389389

390+
match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support())
391+
{
392+
// Set up the small-data optimization limit for architectures that use
393+
// an LLVM module flag to control this.
394+
(Some(threshold), SmallDataThresholdSupport::LlvmModuleFlag(flag)) => {
395+
let flag = SmallCStr::new(flag.as_ref());
396+
unsafe {
397+
llvm::LLVMRustAddModuleFlagU32(
398+
llmod,
399+
llvm::LLVMModFlagBehavior::Error,
400+
flag.as_c_str().as_ptr(),
401+
threshold as u32,
402+
)
403+
}
404+
}
405+
_ => (),
406+
};
407+
390408
// Insert `llvm.ident` metadata.
391409
//
392410
// On the wasm targets it will get hooked up to the "producer" sections

Diff for: compiler/rustc_codegen_llvm/src/llvm_util.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::bug;
1414
use rustc_session::config::{PrintKind, PrintRequest};
1515
use rustc_session::Session;
1616
use rustc_span::symbol::Symbol;
17-
use rustc_target::spec::{MergeFunctions, PanicStrategy};
17+
use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
1818
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
1919

2020
use crate::back::write::create_informational_target_machine;
@@ -125,6 +125,18 @@ unsafe fn configure_llvm(sess: &Session) {
125125
for arg in sess_args {
126126
add(&(*arg), true);
127127
}
128+
129+
match (
130+
sess.opts.unstable_opts.small_data_threshold,
131+
sess.target.small_data_threshold_support(),
132+
) {
133+
// Set up the small-data optimization limit for architectures that use
134+
// an LLVM argument to control this.
135+
(Some(threshold), SmallDataThresholdSupport::LlvmArg(arg)) => {
136+
add(&format!("--{arg}={threshold}"), false)
137+
}
138+
_ => (),
139+
};
128140
}
129141

130142
if sess.opts.unstable_opts.llvm_time_trace {

Diff for: compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@ fn test_unstable_options_tracking_hash() {
847847
tracked!(share_generics, Some(true));
848848
tracked!(show_span, Some(String::from("abc")));
849849
tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
850+
tracked!(small_data_threshold, Some(16));
850851
tracked!(split_lto_unit, Some(true));
851852
tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
852853
tracked!(stack_protector, StackProtector::All);

Diff for: compiler/rustc_session/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ session_split_lto_unit_requires_lto = `-Zsplit-lto-unit` requires `-Clto`, `-Clt
113113
114114
session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`
115115
116+
session_target_small_data_threshold_not_supported = `-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored
117+
116118
session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored
117119
118120
session_unleashed_feature_help_named = skipping check for `{$gate}` feature

Diff for: compiler/rustc_session/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> {
182182
pub(crate) target_triple: &'a TargetTriple,
183183
}
184184

185+
#[derive(Diagnostic)]
186+
#[diag(session_target_small_data_threshold_not_supported)]
187+
pub(crate) struct SmallDataThresholdNotSupportedForTarget<'a> {
188+
pub(crate) target_triple: &'a TargetTriple,
189+
}
190+
185191
#[derive(Diagnostic)]
186192
#[diag(session_branch_protection_requires_aarch64)]
187193
pub(crate) struct BranchProtectionRequiresAArch64;

Diff for: compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,8 @@ written to standard error output)"),
20142014
simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
20152015
"simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \
20162016
to rust's source base directory. only meant for testing purposes"),
2017+
small_data_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
2018+
"Set the threshold for objects to be stored in a \"small data\" section"),
20172019
span_debug: bool = (false, parse_bool, [UNTRACKED],
20182020
"forward proc_macro::Span's `Debug` impl to `Span`"),
20192021
/// o/w tests have closure@path

Diff for: compiler/rustc_session/src/session.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
3030
use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol};
3131
use rustc_target::asm::InlineAsmArch;
3232
use rustc_target::spec::{
33-
CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo,
34-
StackProtector, Target, TargetTriple, TlsModel,
33+
CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
34+
SmallDataThresholdSupport, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel,
3535
};
3636

3737
use crate::code_stats::CodeStats;
@@ -1278,6 +1278,14 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
12781278
}
12791279
}
12801280

1281+
if sess.opts.unstable_opts.small_data_threshold.is_some() {
1282+
if sess.target.small_data_threshold_support() == SmallDataThresholdSupport::None {
1283+
sess.dcx().emit_warn(errors::SmallDataThresholdNotSupportedForTarget {
1284+
target_triple: &sess.opts.target_triple,
1285+
})
1286+
}
1287+
}
1288+
12811289
if sess.opts.unstable_opts.branch_protection.is_some() && sess.target.arch != "aarch64" {
12821290
sess.dcx().emit_err(errors::BranchProtectionRequiresAArch64);
12831291
}

Diff for: compiler/rustc_target/src/spec/mod.rs

+76
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,43 @@ impl ToJson for RelroLevel {
855855
}
856856
}
857857

858+
#[derive(Clone, Debug, PartialEq, Hash)]
859+
pub enum SmallDataThresholdSupport {
860+
None,
861+
DefaultForArch,
862+
LlvmModuleFlag(StaticCow<str>),
863+
LlvmArg(StaticCow<str>),
864+
}
865+
866+
impl FromStr for SmallDataThresholdSupport {
867+
type Err = ();
868+
869+
fn from_str(s: &str) -> Result<Self, Self::Err> {
870+
if s == "none" {
871+
Ok(Self::None)
872+
} else if s == "default-for-arch" {
873+
Ok(Self::DefaultForArch)
874+
} else if let Some(flag) = s.strip_prefix("llvm-module-flag=") {
875+
Ok(Self::LlvmModuleFlag(flag.to_string().into()))
876+
} else if let Some(arg) = s.strip_prefix("llvm-arg=") {
877+
Ok(Self::LlvmArg(arg.to_string().into()))
878+
} else {
879+
Err(())
880+
}
881+
}
882+
}
883+
884+
impl ToJson for SmallDataThresholdSupport {
885+
fn to_json(&self) -> Value {
886+
match self {
887+
Self::None => "none".to_json(),
888+
Self::DefaultForArch => "default-for-arch".to_json(),
889+
Self::LlvmModuleFlag(flag) => format!("llvm-module-flag={flag}").to_json(),
890+
Self::LlvmArg(arg) => format!("llvm-arg={arg}").to_json(),
891+
}
892+
}
893+
}
894+
858895
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
859896
pub enum MergeFunctions {
860897
Disabled,
@@ -2392,6 +2429,9 @@ pub struct TargetOptions {
23922429

23932430
/// Whether the target supports XRay instrumentation.
23942431
pub supports_xray: bool,
2432+
2433+
/// Whether the targets supports -Z small-data-threshold
2434+
small_data_threshold_support: SmallDataThresholdSupport,
23952435
}
23962436

23972437
/// Add arguments for the given flavor and also for its "twin" flavors
@@ -2609,6 +2649,7 @@ impl Default for TargetOptions {
26092649
entry_name: "main".into(),
26102650
entry_abi: Conv::C,
26112651
supports_xray: false,
2652+
small_data_threshold_support: SmallDataThresholdSupport::DefaultForArch,
26122653
}
26132654
}
26142655
}
@@ -2884,6 +2925,15 @@ impl Target {
28842925
Some(Ok(()))
28852926
})).unwrap_or(Ok(()))
28862927
} );
2928+
($key_name:ident, SmallDataThresholdSupport) => ( {
2929+
obj.remove("small-data-threshold-support").and_then(|o| o.as_str().and_then(|s| {
2930+
match s.parse::<SmallDataThresholdSupport>() {
2931+
Ok(support) => base.small_data_threshold_support = support,
2932+
_ => return Some(Err(format!("'{s}' is not a valid value for small-data-threshold-support."))),
2933+
}
2934+
Some(Ok(()))
2935+
})).unwrap_or(Ok(()))
2936+
} );
28872937
($key_name:ident, PanicStrategy) => ( {
28882938
let name = (stringify!($key_name)).replace("_", "-");
28892939
obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
@@ -3321,6 +3371,7 @@ impl Target {
33213371
key!(supported_sanitizers, SanitizerSet)?;
33223372
key!(generate_arange_section, bool);
33233373
key!(supports_stack_protector, bool);
3374+
key!(small_data_threshold_support, SmallDataThresholdSupport)?;
33243375
key!(entry_name);
33253376
key!(entry_abi, Conv)?;
33263377
key!(supports_xray, bool);
@@ -3415,6 +3466,30 @@ impl Target {
34153466
}
34163467
}
34173468
}
3469+
3470+
/// Return the target's small data threshold support, converting
3471+
/// `DefaultForArch` into a concrete value.
3472+
pub fn small_data_threshold_support(&self) -> SmallDataThresholdSupport {
3473+
match &self.options.small_data_threshold_support {
3474+
// Avoid having to duplicate the small data support in every
3475+
// target file by supporting a default value for each
3476+
// architecture.
3477+
SmallDataThresholdSupport::DefaultForArch => match self.arch.as_ref() {
3478+
"mips" | "mips64" | "mips32r6" => {
3479+
SmallDataThresholdSupport::LlvmArg("mips-ssection-threshold".into())
3480+
}
3481+
"hexagon" => {
3482+
SmallDataThresholdSupport::LlvmArg("hexagon-small-data-threshold".into())
3483+
}
3484+
"m68k" => SmallDataThresholdSupport::LlvmArg("m68k-ssection-threshold".into()),
3485+
"riscv32" | "riscv64" => {
3486+
SmallDataThresholdSupport::LlvmModuleFlag("SmallDataLimit".into())
3487+
}
3488+
_ => SmallDataThresholdSupport::None,
3489+
},
3490+
s => s.clone(),
3491+
}
3492+
}
34183493
}
34193494

34203495
impl ToJson for Target {
@@ -3577,6 +3652,7 @@ impl ToJson for Target {
35773652
target_option_val!(c_enum_min_bits);
35783653
target_option_val!(generate_arange_section);
35793654
target_option_val!(supports_stack_protector);
3655+
target_option_val!(small_data_threshold_support);
35803656
target_option_val!(entry_name);
35813657
target_option_val!(entry_abi);
35823658
target_option_val!(supports_xray);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# `small-data-threshold`
2+
3+
-----------------------
4+
5+
This flag controls the maximum static variable size that may be included in the
6+
"small data sections" (.sdata, .sbss) supported by some architectures (RISCV,
7+
MIPS, M68K, Hexagon). Can be set to `0` to disable the use of small data
8+
sections.
9+
10+
Target support is indicated by the `small_data_threshold_support` target
11+
option which can be:
12+
13+
- `none` (`SmallDataThresholdSupport::None`) for no support
14+
- `default-for-arch` (`SmallDataThresholdSupport::DefaultForArch`) which
15+
is automatically translated into an appropriate value for the target.
16+
- `llvm-module-flag=<flag_name>`
17+
(`SmallDataThresholdSupport::LlvmModuleFlag`) for specifying the
18+
threshold via an LLVM module flag
19+
- `llvm-arg=<arg_name>` (`SmallDataThresholdSupport::LlvmArg`) for
20+
specifying the threshold via an LLVM argument.

Diff for: tests/assembly/small_data_threshold.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Test for -Z small_data_threshold=...
2+
//@ revisions: RISCV MIPS HEXAGON M68K
3+
//@ assembly-output: emit-asm
4+
//@ compile-flags: -Z small_data_threshold=4
5+
//@ [RISCV] compile-flags: --target=riscv32im-unknown-none-elf
6+
//@ [RISCV] needs-llvm-components: riscv
7+
//@ [MIPS] compile-flags: --target=mips-unknown-linux-uclibc -C relocation-model=static
8+
//@ [MIPS] compile-flags: -C llvm-args=-mgpopt -C llvm-args=-mlocal-sdata
9+
//@ [MIPS] compile-flags: -C target-feature=+noabicalls
10+
//@ [MIPS] needs-llvm-components: mips
11+
//@ [HEXAGON] compile-flags: --target=hexagon-unknown-linux-musl -C target-feature=+small-data
12+
//@ [HEXAGON] compile-flags: -C llvm-args=--hexagon-statics-in-small-data
13+
//@ [HEXAGON] needs-llvm-components: hexagon
14+
//@ [M68K] compile-flags: --target=m68k-unknown-linux-gnu
15+
//@ [M68K] needs-llvm-components: m68k
16+
17+
#![feature(no_core, lang_items)]
18+
#![no_std]
19+
#![no_core]
20+
#![crate_type = "lib"]
21+
22+
#[lang = "sized"]
23+
trait Sized {}
24+
25+
#[lang = "drop_in_place"]
26+
fn drop_in_place<T>(_: *mut T) {}
27+
28+
#[used]
29+
#[no_mangle]
30+
// U is below the threshold, should be in sdata
31+
static mut U: u16 = 123;
32+
33+
#[used]
34+
#[no_mangle]
35+
// V is below the threshold, should be in sbss
36+
static mut V: u16 = 0;
37+
38+
#[used]
39+
#[no_mangle]
40+
// W is at the threshold, should be in sdata
41+
static mut W: u32 = 123;
42+
43+
#[used]
44+
#[no_mangle]
45+
// X is at the threshold, should be in sbss
46+
static mut X: u32 = 0;
47+
48+
#[used]
49+
#[no_mangle]
50+
// Y is over the threshold, should be in its own .data section
51+
static mut Y: u64 = 123;
52+
53+
#[used]
54+
#[no_mangle]
55+
// Z is over the threshold, should be in its own .bss section
56+
static mut Z: u64 = 0;
57+
58+
// Currently, only MIPS and RISCV successfully put any objects in the small data
59+
// sections so the U/V/W/X tests are skipped on Hexagon and M68K
60+
61+
//@ RISCV: .section .sdata,
62+
//@ RISCV-NOT: .section
63+
//@ RISCV: U:
64+
//@ RISCV: .section .sbss
65+
//@ RISCV-NOT: .section
66+
//@ RISCV: V:
67+
//@ RISCV: .section .sdata
68+
//@ RISCV-NOT: .section
69+
//@ RISCV: W:
70+
//@ RISCV: .section .sbss
71+
//@ RISCV-NOT: .section
72+
//@ RISCV: X:
73+
74+
//@ MIPS: .section .sdata,
75+
//@ MIPS-NOT: .section
76+
//@ MIPS: U:
77+
//@ MIPS: .section .sbss
78+
//@ MIPS-NOT: .section
79+
//@ MIPS: V:
80+
//@ MIPS: .section .sdata
81+
//@ MIPS-NOT: .section
82+
//@ MIPS: W:
83+
//@ MIPS: .section .sbss
84+
//@ MIPS-NOT: .section
85+
//@ MIPS: X:
86+
87+
//@ CHECK: .section .data.Y,
88+
//@ CHECK-NOT: .section
89+
//@ CHECK: Y:
90+
//@ CHECK: .section .bss.Z,
91+
//@ CHECK-NOT: .section
92+
//@ CHECK: Z:

0 commit comments

Comments
 (0)