Skip to content

Commit b8e230a

Browse files
authored
Rollup merge of rust-lang#134030 - folkertdev:min-fn-align, r=workingjubilee
add `-Zmin-function-alignment` tracking issue: rust-lang#82232 This PR adds the `-Zmin-function-alignment=<align>` flag, that specifies a minimum alignment for all* functions. ### Motivation This feature is requested by RfL [here](rust-lang#128830): > i.e. the equivalents of `-fmin-function-alignment` ([GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn), Clang does not support it) / `-falign-functions` ([GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-falign-functions), [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions)). > > For the Linux kernel, the behavior wanted is that of GCC's `-fmin-function-alignment` and Clang's `-falign-functions`, i.e. align all functions, including cold functions. > > There is [`feature(fn_align)`](rust-lang#82232), but we need to do it globally. ### Behavior The `fn_align` feature does not have an RFC. It was decided at the time that it would not be necessary, but maybe we feel differently about that now? In any case, here are the semantics of this flag: - `-Zmin-function-alignment=<align>` specifies the minimum alignment of all* functions - the `#[repr(align(<align>))]` attribute can be used to override the function alignment on a per-function basis: when `-Zmin-function-alignment` is specified, the attribute's value is only used when it is higher than the value passed to `-Zmin-function-alignment`. - the target may decide to use a higher value (e.g. on x86_64 the minimum that LLVM generates is 16) - The highest supported alignment in rust is `2^29`: I checked a bunch of targets, and they all emit the `.p2align 29` directive for targets that align functions at all (some GPU stuff does not have function alignment). *: Only with `build-std` would the minimum alignment also be applied to `std` functions. --- cc `@ojeda` r? `@workingjubilee` you were active on the tracking issue
2 parents fb65a3e + 47573bf commit b8e230a

File tree

8 files changed

+145
-3
lines changed

8 files changed

+145
-3
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,11 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
474474
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
475475
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
476476
}
477-
if let Some(align) = codegen_fn_attrs.alignment {
477+
// function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
478+
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
479+
if let Some(align) =
480+
Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment)
481+
{
478482
llvm::set_alignment(llfn, align);
479483
}
480484
if let Some(backchain) = backchain_attr(cx) {

compiler/rustc_codegen_ssa/src/mir/naked_asm.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,13 @@ fn prefix_and_suffix<'tcx>(
132132

133133
let attrs = tcx.codegen_fn_attrs(instance.def_id());
134134
let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
135-
let align = attrs.alignment.map(|a| a.bytes()).unwrap_or(4);
136135

137-
// See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives.
136+
// function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
137+
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
138+
// if no alignment is specified, an alignment of 4 bytes is used.
139+
let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment;
140+
let align = Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);
141+
138142
// In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
139143
let (arch_prefix, arch_suffix) = if is_arm {
140144
(

compiler/rustc_interface/src/tests.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, filesearc
2424
use rustc_span::edition::{DEFAULT_EDITION, Edition};
2525
use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
2626
use rustc_span::{FileName, SourceFileHashAlgorithm, sym};
27+
use rustc_target::abi::Align;
2728
use rustc_target::spec::{
2829
CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
2930
RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, WasmCAbi,
@@ -807,6 +808,7 @@ fn test_unstable_options_tracking_hash() {
807808
tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
808809
tracked!(maximal_hir_to_mir_coverage, true);
809810
tracked!(merge_functions, Some(MergeFunctions::Disabled));
811+
tracked!(min_function_alignment, Some(Align::EIGHT));
810812
tracked!(mir_emit_retag, true);
811813
tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]);
812814
tracked!(mir_keep_place_mention, true);

compiler/rustc_session/src/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,7 @@ pub(crate) mod dep_tracking {
28922892
use std::num::NonZero;
28932893
use std::path::PathBuf;
28942894

2895+
use rustc_abi::Align;
28952896
use rustc_data_structures::fx::FxIndexMap;
28962897
use rustc_data_structures::stable_hasher::Hash64;
28972898
use rustc_errors::LanguageIdentifier;
@@ -3012,6 +3013,7 @@ pub(crate) mod dep_tracking {
30123013
InliningThreshold,
30133014
FunctionReturn,
30143015
WasmCAbi,
3016+
Align,
30153017
);
30163018

30173019
impl<T1, T2> DepTrackingHash for (T1, T2)

compiler/rustc_session/src/options.rs

+19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::num::{IntErrorKind, NonZero};
44
use std::path::PathBuf;
55
use std::str;
66

7+
use rustc_abi::Align;
78
use rustc_data_structures::fx::FxIndexMap;
89
use rustc_data_structures::profiling::TimePassesFormat;
910
use rustc_data_structures::stable_hasher::Hash64;
@@ -482,6 +483,7 @@ mod desc {
482483
pub(crate) const parse_wasm_c_abi: &str = "`legacy` or `spec`";
483484
pub(crate) const parse_mir_include_spans: &str =
484485
"either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)";
486+
pub(crate) const parse_align: &str = "a number that is a power of 2 between 1 and 2^29";
485487
}
486488

487489
pub mod parse {
@@ -1561,6 +1563,21 @@ pub mod parse {
15611563

15621564
true
15631565
}
1566+
1567+
pub(crate) fn parse_align(slot: &mut Option<Align>, v: Option<&str>) -> bool {
1568+
let mut bytes = 0u64;
1569+
if !parse_number(&mut bytes, v) {
1570+
return false;
1571+
}
1572+
1573+
let Ok(align) = Align::from_bytes(bytes) else {
1574+
return false;
1575+
};
1576+
1577+
*slot = Some(align);
1578+
1579+
true
1580+
}
15641581
}
15651582

15661583
options! {
@@ -1921,6 +1938,8 @@ options! {
19211938
"gather metadata statistics (default: no)"),
19221939
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
19231940
"the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
1941+
min_function_alignment: Option<Align> = (None, parse_align, [TRACKED],
1942+
"align all functions to at least this many bytes. Must be a power of 2"),
19241943
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
19251944
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
19261945
(default: no)"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# `min-function-alignment`
2+
3+
The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/82232.
4+
5+
------------------------
6+
7+
The `-Zmin-function-alignment=<align>` flag specifies the minimum alignment of functions for which code is generated.
8+
The `align` value must be a power of 2, other values are rejected.
9+
10+
Note that `-Zbuild-std` (or similar) is required to apply this minimum alignment to standard library functions.
11+
By default, these functions come precompiled and their alignments won't respect the `min-function-alignment` flag.
12+
13+
This flag is equivalent to:
14+
15+
- `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn)
16+
- `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions)
17+
18+
The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`repr(align(...))`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[repr(align(<align>))]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`.
19+
20+
There are two additional edge cases for this flag:
21+
22+
- targets have a minimum alignment for functions (e.g. on x86_64 the lowest that LLVM generates is 16 bytes).
23+
A `min-function-alignment` value lower than the target's minimum has no effect.
24+
- the maximum alignment supported by rust (and LLVM) is `2^29`. Trying to set a higher value results in an error.
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//@ revisions: align16 align1024
2+
//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0
3+
//@ [align16] compile-flags: -Zmin-function-alignment=16
4+
//@ [align1024] compile-flags: -Zmin-function-alignment=1024
5+
6+
#![crate_type = "lib"]
7+
#![feature(fn_align)]
8+
9+
// functions without explicit alignment use the global minimum
10+
//
11+
// CHECK-LABEL: @no_explicit_align
12+
// align16: align 16
13+
// align1024: align 1024
14+
#[no_mangle]
15+
pub fn no_explicit_align() {}
16+
17+
// CHECK-LABEL: @lower_align
18+
// align16: align 16
19+
// align1024: align 1024
20+
#[no_mangle]
21+
#[repr(align(8))]
22+
pub fn lower_align() {}
23+
24+
// the higher value of min-function-alignment and repr(align) wins out
25+
//
26+
// CHECK-LABEL: @higher_align
27+
// align16: align 32
28+
// align1024: align 1024
29+
#[no_mangle]
30+
#[repr(align(32))]
31+
pub fn higher_align() {}
32+
33+
// cold functions follow the same rules as other functions
34+
//
35+
// in GCC, the `-falign-functions` does not apply to cold functions, but
36+
// `-Zmin-function-alignment` applies to all functions.
37+
//
38+
// CHECK-LABEL: @no_explicit_align_cold
39+
// align16: align 16
40+
// align1024: align 1024
41+
#[no_mangle]
42+
#[cold]
43+
pub fn no_explicit_align_cold() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmin-function-alignment=16
2+
//@ needs-asm-support
3+
//@ ignore-arm no "ret" mnemonic
4+
5+
#![feature(naked_functions, fn_align)]
6+
#![crate_type = "lib"]
7+
8+
// functions without explicit alignment use the global minimum
9+
//
10+
// CHECK: .balign 16
11+
#[no_mangle]
12+
#[naked]
13+
pub unsafe extern "C" fn naked_no_explicit_align() {
14+
core::arch::naked_asm!("ret")
15+
}
16+
17+
// CHECK: .balign 16
18+
#[no_mangle]
19+
#[repr(align(8))]
20+
#[naked]
21+
pub unsafe extern "C" fn naked_lower_align() {
22+
core::arch::naked_asm!("ret")
23+
}
24+
25+
// CHECK: .balign 32
26+
#[no_mangle]
27+
#[repr(align(32))]
28+
#[naked]
29+
pub unsafe extern "C" fn naked_higher_align() {
30+
core::arch::naked_asm!("ret")
31+
}
32+
33+
// cold functions follow the same rules as other functions
34+
//
35+
// in GCC, the `-falign-functions` does not apply to cold functions, but
36+
// `-Zmin-function-alignment` applies to all functions.
37+
//
38+
// CHECK: .balign 16
39+
#[no_mangle]
40+
#[cold]
41+
#[naked]
42+
pub unsafe extern "C" fn no_explicit_align_cold() {
43+
core::arch::naked_asm!("ret")
44+
}

0 commit comments

Comments
 (0)