mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
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:
commit
3f606ceaec
@ -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), .. }
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user