未验证 提交 dfd30d7b 编写于 作者: G Guillaume Gomez 提交者: GitHub

Rollup merge of #86652 - nagisa:nagisa/non-leaf-fp, r=petrochenkov

Add support for leaf function frame pointer elimination

This PR adds ability for the target specifications to specify frame
pointer emission type that's not just “always” or “whatever cg decides”.

In particular there's a new mode that allows omission of the frame
pointer for leaf functions (those that don't call any other functions).

We then set this new mode for Aarch64-based Apple targets.

Fixes #86196
......@@ -12,7 +12,7 @@
use rustc_session::config::OptLevel;
use rustc_session::Session;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::{SanitizerSet, StackProbeType};
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType};
use crate::attributes;
use crate::llvm::AttributePlace::Function;
......@@ -69,15 +69,25 @@ fn naked(val: &'ll Value, is_naked: bool) {
Attribute::Naked.toggle_llfn(Function, val, is_naked);
}
pub fn set_frame_pointer_elimination(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
if cx.sess().must_not_eliminate_frame_pointers() {
llvm::AddFunctionAttrStringValue(
llfn,
llvm::AttributePlace::Function,
cstr!("frame-pointer"),
cstr!("all"),
);
pub fn set_frame_pointer_type(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
let mut fp = cx.sess().target.frame_pointer;
// "mcount" function relies on stack pointer.
// See <https://sourceware.org/binutils/docs/gprof/Implementation.html>.
if cx.sess().instrument_mcount() || matches!(cx.sess().opts.cg.force_frame_pointers, Some(true))
{
fp = FramePointer::Always;
}
let attr_value = match fp {
FramePointer::Always => cstr!("all"),
FramePointer::NonLeaf => cstr!("non-leaf"),
FramePointer::MayOmit => return,
};
llvm::AddFunctionAttrStringValue(
llfn,
llvm::AttributePlace::Function,
cstr!("frame-pointer"),
attr_value,
);
}
/// Tell LLVM what instrument function to insert.
......@@ -254,7 +264,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
}
// FIXME: none of these three functions interact with source level attributes.
set_frame_pointer_elimination(cx, llfn);
set_frame_pointer_type(cx, llfn);
set_instrument_function(cx, llfn);
set_probestack(cx, llfn);
......
......@@ -410,8 +410,8 @@ fn used_statics(&self) -> &RefCell<Vec<&'ll Value>> {
&self.used_statics
}
fn set_frame_pointer_elimination(&self, llfn: &'ll Value) {
attributes::set_frame_pointer_elimination(self, llfn)
fn set_frame_pointer_type(&self, llfn: &'ll Value) {
attributes::set_frame_pointer_type(self, llfn)
}
fn apply_target_cpu_attr(&self, llfn: &'ll Value) {
......
......@@ -674,7 +674,7 @@ fn gen_fn<'ll, 'tcx>(
) -> &'ll Value {
let fn_abi = FnAbi::of_fn_ptr(cx, rust_fn_sig, &[]);
let llfn = cx.declare_fn(name, &fn_abi);
cx.set_frame_pointer_elimination(llfn);
cx.set_frame_pointer_type(llfn);
cx.apply_target_cpu_attr(llfn);
// FIXME(eddyb) find a nicer way to do this.
unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
......
......@@ -406,7 +406,7 @@ fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
};
// `main` should respect same config for frame pointer elimination as rest of code
cx.set_frame_pointer_elimination(llfn);
cx.set_frame_pointer_type(llfn);
cx.apply_target_cpu_attr(llfn);
let llbb = Bx::append_block(&cx, llfn, "top");
......
......@@ -16,7 +16,7 @@ fn vtables(
fn sess(&self) -> &Session;
fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx>;
fn used_statics(&self) -> &RefCell<Vec<Self::Value>>;
fn set_frame_pointer_elimination(&self, llfn: Self::Function);
fn set_frame_pointer_type(&self, llfn: Self::Function);
fn apply_target_cpu_attr(&self, llfn: Self::Function);
fn create_used_variable(&self);
/// Declares the extern "C" main function for the entry point. Returns None if the symbol already exists.
......
......@@ -792,18 +792,6 @@ pub fn target_can_use_split_dwarf(&self) -> bool {
!self.target.is_like_windows && !self.target.is_like_osx
}
pub fn must_not_eliminate_frame_pointers(&self) -> bool {
// "mcount" function relies on stack pointer.
// See <https://sourceware.org/binutils/docs/gprof/Implementation.html>.
if self.instrument_mcount() {
true
} else if let Some(x) = self.opts.cg.force_frame_pointers {
x
} else {
!self.target.eliminate_frame_pointer
}
}
pub fn must_emit_unwind_tables(&self) -> bool {
// This is used to control the emission of the `uwtable` attribute on
// LLVM functions.
......
use crate::spec::{LinkerFlavor, SanitizerSet, Target, TargetOptions};
use crate::spec::{FramePointer, LinkerFlavor, SanitizerSet, Target, TargetOptions};
pub fn target() -> Target {
let mut base = super::apple_base::opts("macos");
......@@ -20,6 +20,10 @@ pub fn target() -> Target {
pointer_width: 64,
data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(),
arch: arch.to_string(),
options: TargetOptions { mcount: "\u{1}mcount".to_string(), ..base },
options: TargetOptions {
mcount: "\u{1}mcount".to_string(),
frame_pointer: FramePointer::NonLeaf,
..base
},
}
}
use super::apple_sdk_base::{opts, Arch};
use crate::spec::{Target, TargetOptions};
use crate::spec::{FramePointer, Target, TargetOptions};
pub fn target() -> Target {
let base = opts("ios", Arch::Arm64);
......@@ -13,6 +13,7 @@ pub fn target() -> Target {
max_atomic_width: Some(128),
unsupported_abis: super::arm_base::unsupported_abis(),
forces_embed_bitcode: true,
frame_pointer: FramePointer::NonLeaf,
// Taken from a clang build on Xcode 11.4.1.
// These arguments are not actually invoked - they just have
// to look right to pass App Store validation.
......
use super::apple_sdk_base::{opts, Arch};
use crate::spec::{Target, TargetOptions};
use crate::spec::{FramePointer, Target, TargetOptions};
pub fn target() -> Target {
let base = opts("ios", Arch::Arm64_macabi);
......@@ -13,6 +13,7 @@ pub fn target() -> Target {
max_atomic_width: Some(128),
unsupported_abis: super::arm_base::unsupported_abis(),
forces_embed_bitcode: true,
frame_pointer: FramePointer::NonLeaf,
// Taken from a clang build on Xcode 11.4.1.
// These arguments are not actually invoked - they just have
// to look right to pass App Store validation.
......
use super::apple_sdk_base::{opts, Arch};
use crate::spec::{Target, TargetOptions};
use crate::spec::{FramePointer, Target, TargetOptions};
pub fn target() -> Target {
let base = opts("ios", Arch::Arm64_sim);
......@@ -21,6 +21,7 @@ pub fn target() -> Target {
max_atomic_width: Some(128),
unsupported_abis: super::arm_base::unsupported_abis(),
forces_embed_bitcode: true,
frame_pointer: FramePointer::NonLeaf,
// Taken from a clang build on Xcode 11.4.1.
// These arguments are not actually invoked - they just have
// to look right to pass App Store validation.
......
use super::apple_sdk_base::{opts, Arch};
use crate::spec::{Target, TargetOptions};
use crate::spec::{FramePointer, Target, TargetOptions};
pub fn target() -> Target {
let base = opts("tvos", Arch::Arm64);
......@@ -13,6 +13,7 @@ pub fn target() -> Target {
max_atomic_width: Some(128),
unsupported_abis: super::arm_base::unsupported_abis(),
forces_embed_bitcode: true,
frame_pointer: FramePointer::NonLeaf,
..base
},
}
......
use std::env;
use crate::spec::{SplitDebuginfo, TargetOptions};
use crate::spec::{FramePointer, SplitDebuginfo, TargetOptions};
pub fn opts(os: &str) -> TargetOptions {
// ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6
......@@ -27,7 +27,7 @@ pub fn opts(os: &str) -> TargetOptions {
families: vec!["unix".to_string()],
is_like_osx: true,
dwarf_version: Some(2),
eliminate_frame_pointer: false,
frame_pointer: FramePointer::Always,
has_rpath: true,
dll_suffix: ".dylib".to_string(),
archive_format: "darwin".to_string(),
......
use crate::spec::{RelroLevel, TargetOptions};
use crate::spec::{FramePointer, RelroLevel, TargetOptions};
pub fn opts() -> TargetOptions {
TargetOptions {
......@@ -8,7 +8,7 @@ pub fn opts() -> TargetOptions {
families: vec!["unix".to_string()],
has_rpath: true,
position_independent_executables: true,
eliminate_frame_pointer: false, // FIXME 43575
frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
relro_level: RelroLevel::Full,
abi_return_struct_as_int: true,
dwarf_version: Some(2),
......
use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};
use crate::spec::{FramePointer, LinkerFlavor, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
let mut base = super::apple_base::opts("macos");
......@@ -8,7 +8,7 @@ pub fn target() -> Target {
base.link_env_remove.extend(super::apple_base::macos_link_env_remove());
// don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
base.stack_probes = StackProbeType::Call;
base.eliminate_frame_pointer = false;
base.frame_pointer = FramePointer::Always;
// Clang automatically chooses a more specific target based on
// MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
......
use crate::spec::{LinkerFlavor, LldFlavor, Target};
use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, Target};
pub fn target() -> Target {
let mut base = super::windows_gnu_base::opts();
......@@ -6,7 +6,7 @@ pub fn target() -> Target {
base.pre_link_args
.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pe".to_string()]);
base.max_atomic_width = Some(64);
base.eliminate_frame_pointer = false; // Required for backtraces
base.frame_pointer = FramePointer::Always; // Required for backtraces
base.linker = Some("i686-w64-mingw32-gcc".to_string());
// Mark all dynamic libraries and executables as compatible with the larger 4GiB address
......
use crate::spec::{LinkerFlavor, StackProbeType, Target};
use crate::spec::{FramePointer, LinkerFlavor, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_musl_base::opts();
......@@ -21,7 +21,7 @@ pub fn target() -> Target {
//
// This may or may not be related to this bug:
// https://llvm.org/bugs/show_bug.cgi?id=30879
base.eliminate_frame_pointer = false;
base.frame_pointer = FramePointer::Always;
Target {
llvm_target: "i686-unknown-linux-musl".to_string(),
......
use crate::spec::{LinkerFlavor, LldFlavor, Target};
use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, Target};
pub fn target() -> Target {
let mut base = super::windows_uwp_gnu_base::opts();
......@@ -6,7 +6,7 @@ pub fn target() -> Target {
base.pre_link_args
.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pe".to_string()]);
base.max_atomic_width = Some(64);
base.eliminate_frame_pointer = false; // Required for backtraces
base.frame_pointer = FramePointer::Always; // Required for backtraces
// Mark all dynamic libraries and executables as compatible with the larger 4GiB address
// space available to x86 Windows binaries on x86_64.
......
use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};
use crate::spec::{FramePointer, LinkArgs, LinkerFlavor, TargetOptions};
use std::default::Default;
pub fn opts() -> TargetOptions {
......@@ -35,7 +35,7 @@ pub fn opts() -> TargetOptions {
is_like_solaris: true,
linker_is_gnu: false,
limit_rdylib_exports: false, // Linker doesn't support this
eliminate_frame_pointer: false,
frame_pointer: FramePointer::Always,
eh_frame_header: false,
late_link_args,
......
use crate::spec::{PanicStrategy, RelocModel, RelroLevel, StackProbeType, TargetOptions};
use crate::spec::TargetOptions;
use crate::spec::{FramePointer, PanicStrategy, RelocModel, RelroLevel, StackProbeType};
pub fn opts() -> TargetOptions {
TargetOptions {
......@@ -7,7 +8,7 @@ pub fn opts() -> TargetOptions {
panic_strategy: PanicStrategy::Abort,
// don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
stack_probes: StackProbeType::Call,
eliminate_frame_pointer: false,
frame_pointer: FramePointer::Always,
position_independent_executables: true,
needs_plt: true,
relro_level: RelroLevel::Full,
......
......@@ -671,6 +671,42 @@ fn to_json(&self) -> Json {
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum FramePointer {
/// Forces the machine code generator to always preserve the frame pointers.
Always,
/// Forces the machine code generator to preserve the frame pointers except for the leaf
/// functions (i.e. those that don't call other functions).
NonLeaf,
/// Allows the machine code generator to omit the frame pointers.
///
/// This option does not guarantee that the frame pointers will be omitted.
MayOmit,
}
impl FromStr for FramePointer {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
Ok(match s {
"always" => Self::Always,
"non-leaf" => Self::NonLeaf,
"may-omit" => Self::MayOmit,
_ => return Err(()),
})
}
}
impl ToJson for FramePointer {
fn to_json(&self) -> Json {
match *self {
Self::Always => "always",
Self::NonLeaf => "non-leaf",
Self::MayOmit => "may-omit",
}
.to_json()
}
}
macro_rules! supported_targets {
( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
$(mod $module;)+
......@@ -1068,8 +1104,8 @@ pub struct TargetOptions {
pub tls_model: TlsModel,
/// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false.
pub disable_redzone: bool,
/// Eliminate frame pointers from stack frames if possible. Defaults to true.
pub eliminate_frame_pointer: bool,
/// Frame pointer mode for this target. Defaults to `MayOmit`.
pub frame_pointer: FramePointer,
/// Emit each function in its own section. Defaults to true.
pub function_sections: bool,
/// String to prepend to the name of every dynamic library. Defaults to "lib".
......@@ -1330,7 +1366,7 @@ fn default() -> TargetOptions {
code_model: None,
tls_model: TlsModel::GeneralDynamic,
disable_redzone: false,
eliminate_frame_pointer: true,
frame_pointer: FramePointer::MayOmit,
function_sections: true,
dll_prefix: "lib".to_string(),
dll_suffix: ".so".to_string(),
......@@ -1833,6 +1869,16 @@ pub fn from_json(mut obj: Json) -> Result<(Target, TargetWarnings), String> {
}
}
if let Some(fp) = obj.remove_key("frame-pointer") {
if let Some(s) = Json::as_string(&fp) {
base.frame_pointer = s
.parse()
.map_err(|()| format!("'{}' is not a valid value for frame-pointer", s))?;
} else {
incorrect_type.push("frame-pointer".to_string())
}
}
key!(is_builtin, bool);
key!(c_int_width = "target-c-int-width");
key!(os);
......@@ -1864,7 +1910,6 @@ pub fn from_json(mut obj: Json) -> Result<(Target, TargetWarnings), String> {
key!(code_model, CodeModel)?;
key!(tls_model, TlsModel)?;
key!(disable_redzone, bool);
key!(eliminate_frame_pointer, bool);
key!(function_sections, bool);
key!(dll_prefix);
key!(dll_suffix);
......@@ -2128,7 +2173,7 @@ fn to_json(&self) -> Json {
target_option_val!(code_model);
target_option_val!(tls_model);
target_option_val!(disable_redzone);
target_option_val!(eliminate_frame_pointer);
target_option_val!(frame_pointer);
target_option_val!(function_sections);
target_option_val!(dll_prefix);
target_option_val!(dll_suffix);
......
use crate::spec::{RelroLevel, TargetOptions};
use crate::spec::{FramePointer, RelroLevel, TargetOptions};
pub fn opts() -> TargetOptions {
TargetOptions {
......@@ -9,7 +9,7 @@ pub fn opts() -> TargetOptions {
has_rpath: true,
abi_return_struct_as_int: true,
position_independent_executables: true,
eliminate_frame_pointer: false, // FIXME 43575
frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
relro_level: RelroLevel::Full,
dwarf_version: Some(2),
..Default::default()
......
......@@ -27,7 +27,8 @@
// differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of
// build scripts / gcc flags.
use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions};
use crate::spec::TargetOptions;
use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
pub fn opts() -> TargetOptions {
// See rust-lang/rfcs#1645 for a discussion about these defaults
......@@ -52,7 +53,7 @@ pub fn opts() -> TargetOptions {
emit_debug_gdb_scripts: false,
// LLVM is eager to trash the link register when calling `noreturn` functions, which
// breaks debugging. Preserve LR by default to prevent that from happening.
eliminate_frame_pointer: false,
frame_pointer: FramePointer::Always,
..Default::default()
}
}
use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target, TargetOptions};
use crate::spec::TargetOptions;
use crate::spec::{FramePointer, LinkerFlavor, SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::apple_base::opts("macos");
base.cpu = "core2".to_string();
base.max_atomic_width = Some(128); // core2 support cmpxchg16b
base.eliminate_frame_pointer = false;
base.frame_pointer = FramePointer::Always;
base.pre_link_args.insert(
LinkerFlavor::Gcc,
vec!["-m64".to_string(), "-arch".to_string(), "x86_64".to_string()],
......
// compile-flags: --crate-type=rlib
// revisions: aarch64-apple aarch64-linux force x64-apple x64-linux
// [aarch64-apple] needs-llvm-components: aarch64
// [aarch64-apple] compile-flags: --target=aarch64-apple-darwin
// [aarch64-linux] needs-llvm-components: aarch64
// [aarch64-linux] compile-flags: --target=aarch64-unknown-linux-gnu
// [force] needs-llvm-components: x86
// [force] compile-flags: --target=x86_64-unknown-linux-gnu -Cforce-frame-pointers=yes
// [x64-apple] needs-llvm-components: x86
// [x64-apple] compile-flags: --target=x86_64-apple-darwin
// [x64-linux] needs-llvm-components: x86
// [x64-linux] compile-flags: --target=x86_64-unknown-linux-gnu
#![feature(no_core, lang_items)]
#![no_core]
#[lang="sized"]
trait Sized { }
#[lang="copy"]
trait Copy { }
// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] {
#[no_mangle]
pub fn peach(x: u32) -> u32 {
x
}
// CHECK: attributes [[PEACH_ATTRS]] = {
// x64-linux-NOT: {{.*}}"frame-pointer"{{.*}}
// aarch64-linux-NOT: {{.*}}"frame-pointer"{{.*}}
// x64-apple-SAME: {{.*}}"frame-pointer"="all"
// force-SAME: {{.*}}"frame-pointer"="all"
// aarch64-apple-SAME: {{.*}}"frame-pointer"="non-leaf"
// CHECK-SAME: }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册