Use call instead of invoke for functions that cannot unwind

The `FnAbi` now knows if the function is allowed to unwind. If a
function isn't allowed to unwind, we can use a `call` instead of an
`invoke`.

This resolves an issue when calling LLVM intrinsics which cannot unwind
LLVM will generate an error if you attempt to invoke them so we need to
ignore cleanup blocks in codegen and generate a call instead.
This commit is contained in:
Wesley Wiser 2020-03-26 05:32:52 -04:00
parent ba72b15666
commit 8da26e0366
3 changed files with 38 additions and 9 deletions

View File

@ -111,7 +111,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
cleanup: Option<mir::BasicBlock>,
) {
if let Some(cleanup) = cleanup {
// If there is a cleanup block and the function we're calling can unwind, then
// do an invoke, otherwise do a call.
if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) {
let ret_bx = if let Some((_, target)) = destination {
fx.blocks[target]
} else {

View File

@ -16,13 +16,13 @@ extern "C" {
#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_0() {
// Ensure that we correctly call foreign C-variadic functions.
// CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM:i32( signext)?]] 0)
// CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM:i32( signext)?]] 0)
foreign_c_variadic_0(0);
// CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42)
// CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42)
foreign_c_variadic_0(0, 42i32);
// CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024)
// CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024)
foreign_c_variadic_0(0, 42i32, 1024i32);
// CHECK: invoke void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024, [[PARAM]] 0)
// CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM]] 0, [[PARAM]] 42, [[PARAM]] 1024, [[PARAM]] 0)
foreign_c_variadic_0(0, 42i32, 1024i32, 0i32);
}
@ -30,24 +30,24 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() {
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
// CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
foreign_c_variadic_1(ap);
}
#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
// CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42)
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42)
foreign_c_variadic_1(ap, 42i32);
}
#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
// CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42)
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42)
foreign_c_variadic_1(ap, 2i32, 42i32);
}
#[unwind(aborts)] // FIXME(#58794)
pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
// CHECK: invoke void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
}

View File

@ -0,0 +1,27 @@
// compile-flags: -C no-prepopulate-passes
#![feature(link_llvm_intrinsics)]
#![crate_type = "lib"]
struct A;
impl Drop for A {
fn drop(&mut self) {
println!("A");
}
}
extern {
#[link_name = "llvm.sqrt.f32"]
fn sqrt(x: f32) -> f32;
}
pub fn do_call() {
let _a = A;
unsafe {
// Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them
// CHECK: call float @llvm.sqrt.f32(float 4.000000e+00
sqrt(4.0);
}
}