Rollup merge of #95864 - luqmana:inline-asm-unwind-store-miscompile, r=Amanieu

Fix miscompilation of inline assembly with outputs in cases where we emit an invoke instead of call instruction.

We ran into this bug where rustc would segfault while trying to compile certain uses of inline assembly.

Here is a simple repro that demonstrates the issue:
```rust
#![feature(asm_unwind)]

fn main() {
    let _x = String::from("string here just cause we need something with a non-trivial drop");
    let foo: u64;
    unsafe {
        std::arch::asm!(
            "mov {}, 1",
            out(reg) foo,
            options(may_unwind)
        );
    }
    println!("{}", foo);
}
```
([playground link](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=7d6641e83370d2536a07234aca2498ff))

But crucially `feature(asm_unwind)` is not actually needed and this can be triggered on stable as a result of the way async functions/generators are handled in the compiler. e.g.:

```rust
extern crate futures; // 0.3.21

async fn bar() {
    let foo: u64;
    unsafe {
        std::arch::asm!(
            "mov {}, 1",
            out(reg) foo,
        );
    }
    println!("{}", foo);
}

fn main() {
    futures::executor::block_on(bar());
}
```
([playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1c7781c34dd4a3e80ae4bd936a0c82fc))

An example of the incorrect LLVM generated:
```llvm
bb1:                                              ; preds = %start
  %1 = invoke i64 asm sideeffect alignstack inteldialect unwind "mov ${0:q}, 1", "=&r,~{dirflag},~{fpsr},~{flags},~{memory}"()
          to label %bb2 unwind label %cleanup, !srcloc !9
  store i64 %1, i64* %foo, align 8

bb2:
[...snip...]
```

The store should not be placed after the asm invoke but rather should be in the normal control flow basic block (`bb2` in this case).

[Here](https://gist.github.com/luqmana/be1af5b64d2cda5a533e3e23a7830b44) is a writeup of the investigation that lead to finding this.
This commit is contained in:
Dylan DPC 2022-04-11 20:00:42 +02:00 committed by GitHub
commit 3f606ceaec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 2 deletions

View File

@ -290,6 +290,11 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
}
attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });
// Switch to the 'normal' basic block if we did an `invoke` instead of a `call`
if let Some((dest, _, _)) = dest_catch_funclet {
self.switch_to_block(dest);
}
// Write results to outputs
for (idx, op) in operands.iter().enumerate() {
if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }

View File

@ -18,10 +18,23 @@ impl Drop for Foo {
}
}
// CHECK-LABEL: @may_unwind
// CHECK-LABEL: @asm_may_unwind
#[no_mangle]
pub unsafe fn may_unwind() {
pub unsafe fn asm_may_unwind() {
let _m = Foo;
// CHECK: invoke void asm sideeffect alignstack inteldialect unwind ""
asm!("", options(may_unwind));
}
// CHECK-LABEL: @asm_with_result_may_unwind
#[no_mangle]
pub unsafe fn asm_with_result_may_unwind() -> u64 {
let _m = Foo;
let res: u64;
// CHECK: [[RES:%[0-9]+]] = invoke i64 asm sideeffect alignstack inteldialect unwind
// CHECK-NEXT: to label %[[NORMALBB:[a-b0-9]+]]
asm!("mov {}, 1", out(reg) res, options(may_unwind));
// CHECK: [[NORMALBB]]:
// CHECK: ret i64 [[RES:%[0-9]+]]
res
}