Skip to content

Commit

Permalink
add -Zmin-function-alignment
Browse files Browse the repository at this point in the history
  • Loading branch information
folkertdev committed Jan 10, 2025
1 parent a52085d commit 47573bf
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 3 deletions.
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,11 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
}
if let Some(align) = codegen_fn_attrs.alignment {
// function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
if let Some(align) =
Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment)
{
llvm::set_alignment(llfn, align);
}
if let Some(backchain) = backchain_attr(cx) {
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,13 @@ fn prefix_and_suffix<'tcx>(

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

// See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives.
// function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
// if no alignment is specified, an alignment of 4 bytes is used.
let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment;
let align = Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);

// In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
let (arch_prefix, arch_suffix) = if is_arm {
(
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, filesearc
use rustc_span::edition::{DEFAULT_EDITION, Edition};
use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
use rustc_span::{FileName, SourceFileHashAlgorithm, sym};
use rustc_target::abi::Align;
use rustc_target::spec::{
CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, WasmCAbi,
Expand Down Expand Up @@ -802,6 +803,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
tracked!(maximal_hir_to_mir_coverage, true);
tracked!(merge_functions, Some(MergeFunctions::Disabled));
tracked!(min_function_alignment, Some(Align::EIGHT));
tracked!(mir_emit_retag, true);
tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]);
tracked!(mir_keep_place_mention, true);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2891,6 +2891,7 @@ pub(crate) mod dep_tracking {
use std::num::NonZero;
use std::path::PathBuf;

use rustc_abi::Align;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::Hash64;
use rustc_errors::LanguageIdentifier;
Expand Down Expand Up @@ -3011,6 +3012,7 @@ pub(crate) mod dep_tracking {
InliningThreshold,
FunctionReturn,
WasmCAbi,
Align,
);

impl<T1, T2> DepTrackingHash for (T1, T2)
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::num::{IntErrorKind, NonZero};
use std::path::PathBuf;
use std::str;

use rustc_abi::Align;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::profiling::TimePassesFormat;
use rustc_data_structures::stable_hasher::Hash64;
Expand Down Expand Up @@ -455,6 +456,7 @@ mod desc {
pub(crate) const parse_wasm_c_abi: &str = "`legacy` or `spec`";
pub(crate) const parse_mir_include_spans: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)";
pub(crate) const parse_align: &str = "a number that is a power of 2 between 1 and 2^29";
}

pub mod parse {
Expand Down Expand Up @@ -1533,6 +1535,21 @@ pub mod parse {

true
}

pub(crate) fn parse_align(slot: &mut Option<Align>, v: Option<&str>) -> bool {
let mut bytes = 0u64;
if !parse_number(&mut bytes, v) {
return false;
}

let Ok(align) = Align::from_bytes(bytes) else {
return false;
};

*slot = Some(align);

true
}
}

options! {
Expand Down Expand Up @@ -1888,6 +1905,8 @@ options! {
"gather metadata statistics (default: no)"),
metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
"the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"),
min_function_alignment: Option<Align> = (None, parse_align, [TRACKED],
"align all functions to at least this many bytes. Must be a power of 2"),
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
(default: no)"),
Expand Down
24 changes: 24 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/min-function-alignment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# `min-function-alignment`

The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/82232.

------------------------

The `-Zmin-function-alignment=<align>` flag specifies the minimum alignment of functions for which code is generated.
The `align` value must be a power of 2, other values are rejected.

Note that `-Zbuild-std` (or similar) is required to apply this minimum alignment to standard library functions.
By default, these functions come precompiled and their alignments won't respect the `min-function-alignment` flag.

This flag is equivalent to:

- `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn)
- `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions)

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`.

There are two additional edge cases for this flag:

- targets have a minimum alignment for functions (e.g. on x86_64 the lowest that LLVM generates is 16 bytes).
A `min-function-alignment` value lower than the target's minimum has no effect.
- the maximum alignment supported by rust (and LLVM) is `2^29`. Trying to set a higher value results in an error.
43 changes: 43 additions & 0 deletions tests/codegen/min-function-alignment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//@ revisions: align16 align1024
//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0
//@ [align16] compile-flags: -Zmin-function-alignment=16
//@ [align1024] compile-flags: -Zmin-function-alignment=1024

#![crate_type = "lib"]
#![feature(fn_align)]

// functions without explicit alignment use the global minimum
//
// CHECK-LABEL: @no_explicit_align
// align16: align 16
// align1024: align 1024
#[no_mangle]
pub fn no_explicit_align() {}

// CHECK-LABEL: @lower_align
// align16: align 16
// align1024: align 1024
#[no_mangle]
#[repr(align(8))]
pub fn lower_align() {}

// the higher value of min-function-alignment and repr(align) wins out
//
// CHECK-LABEL: @higher_align
// align16: align 32
// align1024: align 1024
#[no_mangle]
#[repr(align(32))]
pub fn higher_align() {}

// cold functions follow the same rules as other functions
//
// in GCC, the `-falign-functions` does not apply to cold functions, but
// `-Zmin-function-alignment` applies to all functions.
//
// CHECK-LABEL: @no_explicit_align_cold
// align16: align 16
// align1024: align 1024
#[no_mangle]
#[cold]
pub fn no_explicit_align_cold() {}
44 changes: 44 additions & 0 deletions tests/codegen/naked-fn/min-function-alignment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmin-function-alignment=16
//@ needs-asm-support
//@ ignore-arm no "ret" mnemonic

#![feature(naked_functions, fn_align)]
#![crate_type = "lib"]

// functions without explicit alignment use the global minimum
//
// CHECK: .balign 16
#[no_mangle]
#[naked]
pub unsafe extern "C" fn naked_no_explicit_align() {
core::arch::naked_asm!("ret")
}

// CHECK: .balign 16
#[no_mangle]
#[repr(align(8))]
#[naked]
pub unsafe extern "C" fn naked_lower_align() {
core::arch::naked_asm!("ret")
}

// CHECK: .balign 32
#[no_mangle]
#[repr(align(32))]
#[naked]
pub unsafe extern "C" fn naked_higher_align() {
core::arch::naked_asm!("ret")
}

// cold functions follow the same rules as other functions
//
// in GCC, the `-falign-functions` does not apply to cold functions, but
// `-Zmin-function-alignment` applies to all functions.
//
// CHECK: .balign 16
#[no_mangle]
#[cold]
#[naked]
pub unsafe extern "C" fn no_explicit_align_cold() {
core::arch::naked_asm!("ret")
}

0 comments on commit 47573bf

Please sign in to comment.