Skip to content

Commit 7c00af7

Browse files
authored
Rollup merge of rust-lang#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.
2 parents b8cff7f + 0b2f360 commit 7c00af7

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

Diff for: compiler/rustc_codegen_llvm/src/asm.rs

+5
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
290290
}
291291
attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });
292292

293+
// Switch to the 'normal' basic block if we did an `invoke` instead of a `call`
294+
if let Some((dest, _, _)) = dest_catch_funclet {
295+
self.switch_to_block(dest);
296+
}
297+
293298
// Write results to outputs
294299
for (idx, op) in operands.iter().enumerate() {
295300
if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }

Diff for: src/test/codegen/asm-may_unwind.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,23 @@ impl Drop for Foo {
1818
}
1919
}
2020

21-
// CHECK-LABEL: @may_unwind
21+
// CHECK-LABEL: @asm_may_unwind
2222
#[no_mangle]
23-
pub unsafe fn may_unwind() {
23+
pub unsafe fn asm_may_unwind() {
2424
let _m = Foo;
2525
// CHECK: invoke void asm sideeffect alignstack inteldialect unwind ""
2626
asm!("", options(may_unwind));
2727
}
28+
29+
// CHECK-LABEL: @asm_with_result_may_unwind
30+
#[no_mangle]
31+
pub unsafe fn asm_with_result_may_unwind() -> u64 {
32+
let _m = Foo;
33+
let res: u64;
34+
// CHECK: [[RES:%[0-9]+]] = invoke i64 asm sideeffect alignstack inteldialect unwind
35+
// CHECK-NEXT: to label %[[NORMALBB:[a-b0-9]+]]
36+
asm!("mov {}, 1", out(reg) res, options(may_unwind));
37+
// CHECK: [[NORMALBB]]:
38+
// CHECK: ret i64 [[RES:%[0-9]+]]
39+
res
40+
}

0 commit comments

Comments
 (0)