Skip to content

Commit 007080b

Browse files
committed
Target stack-probe support configurable finely
This adds capability to configure the target's stack probe support in a more precise manner than just on/off. In particular now we allow choosing between always inline-asm, always call or either one of those depending on the LLVM version on a per-target basis.
1 parent efdb859 commit 007080b

35 files changed

+203
-87
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+26-19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_middle::ty::query::Providers;
1313
use rustc_middle::ty::{self, TyCtxt};
1414
use rustc_session::config::{OptLevel, SanitizerSet};
1515
use rustc_session::Session;
16+
use rustc_target::spec::StackProbeType;
1617

1718
use crate::attributes;
1819
use crate::llvm::AttributePlace::Function;
@@ -98,12 +99,6 @@ fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
9899
}
99100

100101
fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
101-
// Only use stack probes if the target specification indicates that we
102-
// should be using stack probes
103-
if !cx.sess().target.stack_probes {
104-
return;
105-
}
106-
107102
// Currently stack probes seem somewhat incompatible with the address
108103
// sanitizer and thread sanitizer. With asan we're already protected from
109104
// stack overflow anyway so we don't really need stack probes regardless.
@@ -127,19 +122,31 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
127122
return;
128123
}
129124

130-
llvm::AddFunctionAttrStringValue(
131-
llfn,
132-
llvm::AttributePlace::Function,
133-
const_cstr!("probe-stack"),
134-
if llvm_util::get_version() < (11, 0, 1) {
135-
// Flag our internal `__rust_probestack` function as the stack probe symbol.
136-
// This is defined in the `compiler-builtins` crate for each architecture.
137-
const_cstr!("__rust_probestack")
138-
} else {
139-
// On LLVM 11+, emit inline asm for stack probes instead of a function call.
140-
const_cstr!("inline-asm")
141-
},
142-
);
125+
let attr_value = match cx.sess().target.stack_probes {
126+
StackProbeType::None => None,
127+
// Request LLVM to generate the probes inline. If the given LLVM version does not support
128+
// this, no probe is generated at all (even if the attribute is specified).
129+
StackProbeType::Inline => Some(const_cstr!("inline-asm")),
130+
// Flag our internal `__rust_probestack` function as the stack probe symbol.
131+
// This is defined in the `compiler-builtins` crate for each architecture.
132+
StackProbeType::Call => Some(const_cstr!("__rust_probestack")),
133+
// Pick from the two above based on the LLVM version.
134+
StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
135+
if llvm_util::get_version() < min_llvm_version_for_inline {
136+
Some(const_cstr!("__rust_probestack"))
137+
} else {
138+
Some(const_cstr!("inline-asm"))
139+
}
140+
}
141+
};
142+
if let Some(attr_value) = attr_value {
143+
llvm::AddFunctionAttrStringValue(
144+
llfn,
145+
llvm::AttributePlace::Function,
146+
const_cstr!("probe-stack"),
147+
attr_value,
148+
);
149+
}
143150
}
144151

145152
pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {

compiler/rustc_target/src/spec/i386_apple_ios.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::apple_sdk_base::{opts, Arch};
2-
use crate::spec::{Target, TargetOptions};
2+
use crate::spec::{StackProbeType, Target, TargetOptions};
33

44
pub fn target() -> Target {
55
let base = opts("ios", Arch::I386);
@@ -10,6 +10,10 @@ pub fn target() -> Target {
1010
f64:32:64-f80:128-n8:16:32-S128"
1111
.to_string(),
1212
arch: "x86".to_string(),
13-
options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base },
13+
options: TargetOptions {
14+
max_atomic_width: Some(64),
15+
stack_probes: StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) },
16+
..base
17+
},
1418
}
1519
}

compiler/rustc_target/src/spec/i686_apple_darwin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use crate::spec::{LinkerFlavor, Target, TargetOptions};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};
22

33
pub fn target() -> Target {
44
let mut base = super::apple_base::opts("macos");
55
base.cpu = "yonah".to_string();
66
base.max_atomic_width = Some(64);
77
base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]);
88
base.link_env_remove.extend(super::apple_base::macos_link_env_remove());
9-
base.stack_probes = true;
9+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
1010
base.eliminate_frame_pointer = false;
1111

1212
// Clang automatically chooses a more specific target based on

compiler/rustc_target/src/spec/i686_linux_android.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::spec::Target;
1+
use crate::spec::{StackProbeType, Target};
22

33
// See https://developer.android.com/ndk/guides/abis.html#x86
44
// for target ABI requirements.
@@ -11,7 +11,7 @@ pub fn target() -> Target {
1111
// http://developer.android.com/ndk/guides/abis.html#x86
1212
base.cpu = "pentiumpro".to_string();
1313
base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".to_string();
14-
base.stack_probes = true;
14+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
1515

1616
Target {
1717
llvm_target: "i686-linux-android".to_string(),

compiler/rustc_target/src/spec/i686_unknown_freebsd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

33
pub fn target() -> Target {
44
let mut base = super::freebsd_base::opts();
@@ -7,7 +7,7 @@ pub fn target() -> Target {
77
let pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap();
88
pre_link_args.push("-m32".to_string());
99
pre_link_args.push("-Wl,-znotext".to_string());
10-
base.stack_probes = true;
10+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
1111

1212
Target {
1313
llvm_target: "i686-unknown-freebsd".to_string(),

compiler/rustc_target/src/spec/i686_unknown_haiku.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

33
pub fn target() -> Target {
44
let mut base = super::haiku_base::opts();
55
base.cpu = "pentium4".to_string();
66
base.max_atomic_width = Some(64);
77
base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]);
8-
base.stack_probes = true;
8+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
99

1010
Target {
1111
llvm_target: "i686-unknown-haiku".to_string(),

compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

33
pub fn target() -> Target {
44
let mut base = super::linux_gnu_base::opts();
55
base.cpu = "pentium4".to_string();
66
base.max_atomic_width = Some(64);
77
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
8-
base.stack_probes = true;
8+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
99

1010
Target {
1111
llvm_target: "i686-unknown-linux-gnu".to_string(),

compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

33
pub fn target() -> Target {
44
let mut base = super::linux_musl_base::opts();
55
base.cpu = "pentium4".to_string();
66
base.max_atomic_width = Some(64);
77
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
88
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,-melf_i386".to_string());
9-
base.stack_probes = true;
9+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
1010

1111
// The unwinder used by i686-unknown-linux-musl, the LLVM libunwind
1212
// implementation, apparently relies on frame pointers existing... somehow.

compiler/rustc_target/src/spec/i686_unknown_netbsd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target, TargetOptions};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};
22

33
pub fn target() -> Target {
44
let mut base = super::netbsd_base::opts();
55
base.cpu = "pentium4".to_string();
66
base.max_atomic_width = Some(64);
77
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
8-
base.stack_probes = true;
8+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
99

1010
Target {
1111
llvm_target: "i686-unknown-netbsdelf".to_string(),

compiler/rustc_target/src/spec/i686_unknown_openbsd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

33
pub fn target() -> Target {
44
let mut base = super::openbsd_base::opts();
55
base.cpu = "pentium4".to_string();
66
base.max_atomic_width = Some(64);
77
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
88
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-fuse-ld=lld".to_string());
9-
base.stack_probes = true;
9+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
1010

1111
Target {
1212
llvm_target: "i686-unknown-openbsd".to_string(),

compiler/rustc_target/src/spec/i686_wrs_vxworks.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

33
pub fn target() -> Target {
44
let mut base = super::vxworks_base::opts();
55
base.cpu = "pentium4".to_string();
66
base.max_atomic_width = Some(64);
77
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
8-
base.stack_probes = true;
8+
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
99

1010
Target {
1111
llvm_target: "i686-unknown-linux-gnu".to_string(),

compiler/rustc_target/src/spec/linux_kernel_base.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, TargetOptions};
1+
use crate::spec::{
2+
LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType, TargetOptions,
3+
};
24

35
pub fn opts() -> TargetOptions {
46
let mut pre_link_args = LinkArgs::new();
@@ -11,7 +13,7 @@ pub fn opts() -> TargetOptions {
1113
env: "gnu".to_string(),
1214
disable_redzone: true,
1315
panic_strategy: PanicStrategy::Abort,
14-
stack_probes: true,
16+
stack_probes: StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) },
1517
eliminate_frame_pointer: false,
1618
linker_is_gnu: true,
1719
position_independent_executables: true,

compiler/rustc_target/src/spec/mod.rs

+94-4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
4040
use rustc_serialize::json::{Json, ToJson};
4141
use rustc_span::symbol::{sym, Symbol};
4242
use std::collections::BTreeMap;
43+
use std::convert::TryFrom;
4344
use std::ops::{Deref, DerefMut};
4445
use std::path::{Path, PathBuf};
4546
use std::str::FromStr;
@@ -479,6 +480,83 @@ macro_rules! supported_targets {
479480
};
480481
}
481482

483+
#[derive(Clone, Debug, PartialEq, Eq)]
484+
pub enum StackProbeType {
485+
/// Don't emit any stack probes.
486+
None,
487+
/// It is harmless to use this option even on targets that do not have backend support for
488+
/// stack probes as the failure mode is the same as if no stack-probe option was specified in
489+
/// the first place.
490+
Inline,
491+
/// Call `__rust_probestack` whenever stack needs to be probed.
492+
Call,
493+
/// Use inline option for LLVM versions later than specified in `min_llvm_version_for_inline`
494+
/// and call `__rust_probestack` otherwise.
495+
InlineOrCall { min_llvm_version_for_inline: (u32, u32, u32) },
496+
}
497+
498+
impl StackProbeType {
499+
fn from_json(json: &Json) -> Result<Self, String> {
500+
let object = json.as_object().ok_or_else(|| "expected a JSON object")?;
501+
let kind = object
502+
.get("kind")
503+
.and_then(|o| o.as_string())
504+
.ok_or_else(|| "expected `kind` to be a string")?;
505+
match kind {
506+
"none" => Ok(StackProbeType::None),
507+
"inline" => Ok(StackProbeType::Inline),
508+
"call" => Ok(StackProbeType::Call),
509+
"inline-or-call" => {
510+
let min_version = object
511+
.get("min-llvm-version-for-inline")
512+
.and_then(|o| o.as_array())
513+
.ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?;
514+
let mut iter = min_version.into_iter().map(|v| {
515+
let int = v.as_u64().ok_or_else(
516+
|| "expected `min-llvm-version-for-inline` values to be integers",
517+
)?;
518+
u32::try_from(int)
519+
.map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32")
520+
});
521+
let min_llvm_version_for_inline = (
522+
iter.next().unwrap_or(Ok(11))?,
523+
iter.next().unwrap_or(Ok(0))?,
524+
iter.next().unwrap_or(Ok(0))?,
525+
);
526+
Ok(StackProbeType::InlineOrCall { min_llvm_version_for_inline })
527+
}
528+
_ => Err(String::from(
529+
"`kind` expected to be one of `inline-or-none`, `call` or `inline-or-call`",
530+
)),
531+
}
532+
}
533+
}
534+
535+
impl ToJson for StackProbeType {
536+
fn to_json(&self) -> Json {
537+
Json::Object(match self {
538+
StackProbeType::None => {
539+
vec![(String::from("kind"), "none".to_json())].into_iter().collect()
540+
}
541+
StackProbeType::Inline => {
542+
vec![(String::from("kind"), "inline".to_json())].into_iter().collect()
543+
}
544+
StackProbeType::Call => {
545+
vec![(String::from("kind"), "call".to_json())].into_iter().collect()
546+
}
547+
StackProbeType::InlineOrCall { min_llvm_version_for_inline } => vec![
548+
(String::from("kind"), "inline-or-call".to_json()),
549+
(
550+
String::from("min-llvm-version-for-inline"),
551+
min_llvm_version_for_inline.to_json(),
552+
),
553+
]
554+
.into_iter()
555+
.collect(),
556+
})
557+
}
558+
}
559+
482560
supported_targets! {
483561
("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu),
484562
("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32),
@@ -926,8 +1004,8 @@ pub struct TargetOptions {
9261004
/// Whether or not crt-static is respected by the compiler (or is a no-op).
9271005
pub crt_static_respected: bool,
9281006

929-
/// Whether or not stack probes (__rust_probestack) are enabled
930-
pub stack_probes: bool,
1007+
/// The implementation of stack probes to use.
1008+
pub stack_probes: StackProbeType,
9311009

9321010
/// The minimum alignment for global symbols.
9331011
pub min_global_align: Option<u64>,
@@ -1085,7 +1163,7 @@ impl Default for TargetOptions {
10851163
crt_static_allows_dylibs: false,
10861164
crt_static_default: false,
10871165
crt_static_respected: false,
1088-
stack_probes: false,
1166+
stack_probes: StackProbeType::None,
10891167
min_global_align: None,
10901168
default_codegen_units: None,
10911169
trap_unreachable: true,
@@ -1361,6 +1439,18 @@ impl Target {
13611439
Some(Ok(()))
13621440
})).unwrap_or(Ok(()))
13631441
} );
1442+
($key_name:ident, StackProbeType) => ( {
1443+
let name = (stringify!($key_name)).replace("_", "-");
1444+
obj.find(&name[..]).and_then(|o| match StackProbeType::from_json(o) {
1445+
Ok(v) => {
1446+
base.$key_name = v;
1447+
Some(Ok(()))
1448+
},
1449+
Err(s) => Some(Err(
1450+
format!("`{:?}` is not a valid value for `{}`: {}", o, name, s)
1451+
)),
1452+
}).unwrap_or(Ok(()))
1453+
} );
13641454
($key_name:ident, crt_objects_fallback) => ( {
13651455
let name = (stringify!($key_name)).replace("_", "-");
13661456
obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
@@ -1516,7 +1606,7 @@ impl Target {
15161606
key!(crt_static_allows_dylibs, bool);
15171607
key!(crt_static_default, bool);
15181608
key!(crt_static_respected, bool);
1519-
key!(stack_probes, bool);
1609+
key!(stack_probes, StackProbeType)?;
15201610
key!(min_global_align, Option<u64>);
15211611
key!(default_codegen_units, Option<u64>);
15221612
key!(trap_unreachable, bool);

compiler/rustc_target/src/spec/uefi_msvc_base.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
1010
// code runs in the same environment, no process separation is supported.
1111

12-
use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions};
12+
use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions};
1313

1414
pub fn opts() -> TargetOptions {
1515
let mut base = super::msvc_base::opts();
@@ -43,7 +43,7 @@ pub fn opts() -> TargetOptions {
4343
exe_suffix: ".efi".to_string(),
4444
allows_weak_linkage: false,
4545
panic_strategy: PanicStrategy::Abort,
46-
stack_probes: true,
46+
stack_probes: StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) },
4747
singlethread: true,
4848
linker: Some("rust-lld".to_string()),
4949
..base

0 commit comments

Comments
 (0)