Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use probe-stack=inline-asm in LLVM 11+ #77885

Merged
merged 1 commit into from
Jan 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,18 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
return;
}

// Flag our internal `__rust_probestack` function as the stack probe symbol.
// This is defined in the `compiler-builtins` crate for each architecture.
llvm::AddFunctionAttrStringValue(
llfn,
llvm::AttributePlace::Function,
const_cstr!("probe-stack"),
const_cstr!("__rust_probestack"),
if llvm_util::get_version() < (11, 0, 1) {
// Flag our internal `__rust_probestack` function as the stack probe symbol.
// This is defined in the `compiler-builtins` crate for each architecture.
const_cstr!("__rust_probestack")
} else {
// On LLVM 11+, emit inline asm for stack probes instead of a function call.
const_cstr!("inline-asm")
},
);
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub unsafe fn create_module(
let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx);

let mut target_data_layout = sess.target.data_layout.clone();
if llvm_util::get_major_version() < 10
if llvm_util::get_version() < (10, 0, 0)
&& (sess.target.arch == "x86" || sess.target.arch == "x86_64")
{
target_data_layout = strip_x86_address_spaces(target_data_layout);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1811,6 +1811,7 @@ extern "C" {
pub fn LLVMRustDebugMetadataVersion() -> u32;
pub fn LLVMRustVersionMajor() -> u32;
pub fn LLVMRustVersionMinor() -> u32;
pub fn LLVMRustVersionPatch() -> u32;

pub fn LLVMRustAddModuleFlag(M: &Module, name: *const c_char, value: u32);

Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,17 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
}

pub fn print_version() {
let (major, minor, patch) = get_version();
println!("LLVM version: {}.{}.{}", major, minor, patch);
}

pub fn get_version() -> (u32, u32, u32) {
// Can be called without initializing LLVM
unsafe {
println!("LLVM version: {}.{}", llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
(llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor(), llvm::LLVMRustVersionPatch())
}
}

pub fn get_major_version() -> u32 {
unsafe { llvm::LLVMRustVersionMajor() }
}

pub fn print_passes() {
// Can be called without initializing LLVM
unsafe {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,8 @@ extern "C" uint32_t LLVMRustDebugMetadataVersion() {
return DEBUG_METADATA_VERSION;
}

extern "C" uint32_t LLVMRustVersionPatch() { return LLVM_VERSION_PATCH; }

extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }

extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
Expand Down
42 changes: 42 additions & 0 deletions src/test/assembly/stack-probes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// min-llvm-version: 11.0.1
// revisions: x86_64 i686
// assembly-output: emit-asm
//[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
//[i686] compile-flags: --target i686-unknown-linux-gnu
nagisa marked this conversation as resolved.
Show resolved Hide resolved
// compile-flags: -C llvm-args=--x86-asm-syntax=intel

#![feature(no_core, lang_items)]
#![crate_type = "lib"]
#![no_core]

#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}

impl Copy for u8 {}

// Check that inline-asm stack probes are generated correctly.
// To avoid making this test fragile to slight asm changes,
// we only check that the stack pointer is decremented by a page at a time,
// instead of matching the whole probe sequence.

// CHECK-LABEL: small_stack_probe:
#[no_mangle]
pub fn small_stack_probe(x: u8, f: fn([u8; 8192])) {
// CHECK-NOT: __rust_probestack
// x86_64: sub rsp, 4096
// i686: sub esp, 4096
let a = [x; 8192];
f(a);
}

// CHECK-LABEL: big_stack_probe:
#[no_mangle]
pub fn big_stack_probe(x: u8, f: fn([u8; 65536])) {
// CHECK-NOT: __rust_probestack
// x86_64: sub rsp, 4096
// i686: sub esp, 4096
let a = [x; 65536];
f(a);
}
3 changes: 2 additions & 1 deletion src/test/codegen/stack-probes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
// ignore-emscripten
// ignore-windows
// compile-flags: -C no-prepopulate-passes
// min-llvm-version: 11.0.1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by the way, doesn't adding this mean that we are effectively no longer unit-testing our old stack-probe strategy, since we will just skip this test if the LLVM version is not sufficiently new?

Maybe we should have two files, one for LLVM < 11.0.1 and one for LLVM >= 11.0.1?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't generally test very meticulously the setups that use LLVM versions outside of what is in the Rust repository. This is reflected in e.g. tests for all our (oft backported) soundness fixes only being applicable to the newest LLVM versions and our fork.

So far it has been the unwritten policy that it is up to the users who build with alternative LLVM versions to ensure that their builds do what they expect. (This is also another of those things that I should eventually look into codifying in some document outlining our LLVM support strategy)


#![crate_type = "lib"]

#[no_mangle]
pub fn foo() {
// CHECK: @foo() unnamed_addr #0
// CHECK: attributes #0 = { {{.*}}"probe-stack"="__rust_probestack"{{.*}} }
// CHECK: attributes #0 = { {{.*}}"probe-stack"="inline-asm"{{.*}} }
}