Fix asm goto with outputs
When outputs are used together with labels, they are considered to be written for all destinations, not only when falling through.
This commit is contained in:
parent
f5d1857685
commit
b8df869ebb
4 changed files with 68 additions and 37 deletions
|
@ -342,24 +342,25 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
||||||
}
|
}
|
||||||
attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });
|
attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });
|
||||||
|
|
||||||
// Switch to the 'normal' basic block if we did an `invoke` instead of a `call`
|
// Write results to outputs. We need to do this for all possible control flow.
|
||||||
if let Some(dest) = dest {
|
for block in Some(dest).into_iter().chain(labels.iter().copied().map(Some)) {
|
||||||
self.switch_to_block(dest);
|
if let Some(block) = block {
|
||||||
}
|
self.switch_to_block(block);
|
||||||
|
}
|
||||||
|
|
||||||
// Write results to outputs
|
for (idx, op) in operands.iter().enumerate() {
|
||||||
for (idx, op) in operands.iter().enumerate() {
|
if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }
|
||||||
if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }
|
| InlineAsmOperandRef::InOut { reg, out_place: Some(place), .. } = *op
|
||||||
| InlineAsmOperandRef::InOut { reg, out_place: Some(place), .. } = *op
|
{
|
||||||
{
|
let value = if output_types.len() == 1 {
|
||||||
let value = if output_types.len() == 1 {
|
result
|
||||||
result
|
} else {
|
||||||
} else {
|
self.extract_value(result, op_idx[&idx] as u64)
|
||||||
self.extract_value(result, op_idx[&idx] as u64)
|
};
|
||||||
};
|
let value =
|
||||||
let value =
|
llvm_fixup_output(self, value, reg.reg_class(), &place.layout, instance);
|
||||||
llvm_fixup_output(self, value, reg.reg_class(), &place.layout, instance);
|
OperandValue::Immediate(value).store(self, place);
|
||||||
OperandValue::Immediate(value).store(self, place);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,6 @@
|
||||||
|
|
||||||
use std::arch::asm;
|
use std::arch::asm;
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn panicky() {}
|
|
||||||
|
|
||||||
struct Foo;
|
|
||||||
|
|
||||||
impl Drop for Foo {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK-LABEL: @asm_goto
|
// CHECK-LABEL: @asm_goto
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn asm_goto() {
|
pub unsafe fn asm_goto() {
|
||||||
|
@ -38,6 +27,19 @@ pub unsafe fn asm_goto_with_outputs() -> u64 {
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @asm_goto_with_outputs_use_in_label
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn asm_goto_with_outputs_use_in_label() -> u64 {
|
||||||
|
let out: u64;
|
||||||
|
// CHECK: [[RES:%[0-9]+]] = callbr i64 asm sideeffect alignstack inteldialect "
|
||||||
|
// CHECK-NEXT: to label %[[FALLTHROUGHBB:[a-b0-9]+]] [label %[[JUMPBB:[a-b0-9]+]]]
|
||||||
|
asm!("{} /* {} */", out(reg) out, label { return out; });
|
||||||
|
// CHECK: [[JUMPBB]]:
|
||||||
|
// CHECK-NEXT: [[RET:%.+]] = phi i64 [ 1, %[[FALLTHROUGHBB]] ], [ [[RES]], %start ]
|
||||||
|
// CHECK-NEXT: ret i64 [[RET]]
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: @asm_goto_noreturn
|
// CHECK-LABEL: @asm_goto_noreturn
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn asm_goto_noreturn() -> u64 {
|
pub unsafe fn asm_goto_noreturn() -> u64 {
|
||||||
|
|
|
@ -31,10 +31,6 @@ fn goto_jump() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// asm goto with outputs cause miscompilation in LLVM. UB can be triggered
|
|
||||||
// when outputs are used inside the label block when optimisation is enabled.
|
|
||||||
// See: https://github.com/llvm/llvm-project/issues/74483
|
|
||||||
/*
|
|
||||||
fn goto_out_fallthrough() {
|
fn goto_out_fallthrough() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut out: usize;
|
let mut out: usize;
|
||||||
|
@ -68,7 +64,38 @@ fn goto_out_jump() {
|
||||||
assert!(value);
|
assert!(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
// asm goto with outputs cause miscompilation in LLVM when multiple outputs are present.
|
||||||
|
// The code sample below is adapted from https://github.com/llvm/llvm-project/issues/74483
|
||||||
|
// and does not work with `-C opt-level=0`
|
||||||
|
#[expect(unused)]
|
||||||
|
fn goto_multi_out() {
|
||||||
|
#[inline(never)]
|
||||||
|
#[allow(unused)]
|
||||||
|
fn goto_multi_out(a: usize, b: usize) -> usize {
|
||||||
|
let mut x: usize;
|
||||||
|
let mut y: usize;
|
||||||
|
let mut z: usize;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov {x}, {a}",
|
||||||
|
"test {a}, {a}",
|
||||||
|
"jnz {label1}",
|
||||||
|
"/* {y} {z} {b} {label2} */",
|
||||||
|
x = out(reg) x,
|
||||||
|
y = out(reg) y,
|
||||||
|
z = out(reg) z,
|
||||||
|
a = in(reg) a,
|
||||||
|
b = in(reg) b,
|
||||||
|
label1 = label { return x },
|
||||||
|
label2 = label { return 1 },
|
||||||
|
);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(goto_multi_out(11, 22), 11);
|
||||||
|
}
|
||||||
|
|
||||||
fn goto_noreturn() {
|
fn goto_noreturn() {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -102,8 +129,9 @@ fn goto_noreturn_diverge() {
|
||||||
fn main() {
|
fn main() {
|
||||||
goto_fallthough();
|
goto_fallthough();
|
||||||
goto_jump();
|
goto_jump();
|
||||||
// goto_out_fallthrough();
|
goto_out_fallthrough();
|
||||||
// goto_out_jump();
|
goto_out_jump();
|
||||||
|
// goto_multi_out();
|
||||||
goto_noreturn();
|
goto_noreturn();
|
||||||
goto_noreturn_diverge();
|
goto_noreturn_diverge();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
warning: unreachable statement
|
warning: unreachable statement
|
||||||
--> $DIR/goto.rs:97:9
|
--> $DIR/goto.rs:124:9
|
||||||
|
|
|
|
||||||
LL | / asm!(
|
LL | / asm!(
|
||||||
LL | | "jmp {}",
|
LL | | "jmp {}",
|
||||||
|
@ -13,7 +13,7 @@ LL | unreachable!();
|
||||||
| ^^^^^^^^^^^^^^ unreachable statement
|
| ^^^^^^^^^^^^^^ unreachable statement
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/goto.rs:87:8
|
--> $DIR/goto.rs:114:8
|
||||||
|
|
|
|
||||||
LL | #[warn(unreachable_code)]
|
LL | #[warn(unreachable_code)]
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue