about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Menage <menage@gmail.com>2024-09-10 12:19:16 -0700
committerPaul Menage <menage@gmail.com>2024-09-10 12:19:16 -0700
commit3810386bbe95e989d399a2d46d806bcf7c607c06 (patch)
treeb403e8996331e4dec86a1e29dfaa8102b9741b10
parent33855f80d4393bff5d1226eabf8e61f348179cee (diff)
downloadrust-3810386bbe95e989d399a2d46d806bcf7c607c06.tar.gz
rust-3810386bbe95e989d399a2d46d806bcf7c607c06.zip
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.
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs20
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs14
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_session/messages.ftl2
-rw-r--r--compiler/rustc_session/src/errors.rs6
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_session/src/session.rs12
-rw-r--r--compiler/rustc_target/src/spec/mod.rs76
-rw-r--r--src/doc/unstable-book/src/compiler-flags/small-data-threshold.md20
-rw-r--r--tests/assembly/small_data_threshold.rs92
10 files changed, 241 insertions, 4 deletions
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 01aae24ab56..73c2c15717f 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -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
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index d55220ba5c3..ea0abb1fa53 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -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 {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 50422df8ee6..baeac6c32f9 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -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);
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 01c371ee498..a4fbab8477b 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -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
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 15bbd4ff7bf..462e2a97c33 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -183,6 +183,12 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> {
 }
 
 #[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;
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index a57dc80b316..5a6fa25fd3d 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -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
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 70430d82ab5..387ae1b78c4 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -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);
     }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index c3e7f74c564..f12e3e595ad 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -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);
diff --git a/src/doc/unstable-book/src/compiler-flags/small-data-threshold.md b/src/doc/unstable-book/src/compiler-flags/small-data-threshold.md
new file mode 100644
index 00000000000..1734433181e
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/small-data-threshold.md
@@ -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.
diff --git a/tests/assembly/small_data_threshold.rs b/tests/assembly/small_data_threshold.rs
new file mode 100644
index 00000000000..b0c0a63ca49
--- /dev/null
+++ b/tests/assembly/small_data_threshold.rs
@@ -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: