Support use of asm goto with outputs and options(noreturn)
When labels are present, the `noreturn` option really means that asm block won't fallthrough -- if labels are present, then outputs can still be meaningfully used.
This commit is contained in:
parent
b8df869ebb
commit
73f8309300
5 changed files with 45 additions and 5 deletions
|
@ -300,7 +300,10 @@ pub fn parse_asm_args<'a>(
|
||||||
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
|
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
|
||||||
dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
|
dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
|
||||||
}
|
}
|
||||||
if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
|
if args.options.contains(ast::InlineAsmOptions::NORETURN)
|
||||||
|
&& !outputs_sp.is_empty()
|
||||||
|
&& labels_sp.is_empty()
|
||||||
|
{
|
||||||
let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
|
let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
|
||||||
// Bail out now since this is likely to confuse MIR
|
// Bail out now since this is likely to confuse MIR
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
|
|
@ -343,7 +343,14 @@ 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 });
|
||||||
|
|
||||||
// Write results to outputs. We need to do this for all possible control flow.
|
// Write results to outputs. We need to do this for all possible control flow.
|
||||||
for block in Some(dest).into_iter().chain(labels.iter().copied().map(Some)) {
|
//
|
||||||
|
// Note that `dest` maybe populated with unreachable_block when asm goto with outputs
|
||||||
|
// is used (because we need to codegen callbr which always needs a destination), so
|
||||||
|
// here we use the NORETURN option to determine if `dest` should be used.
|
||||||
|
for block in (if options.contains(InlineAsmOptions::NORETURN) { None } else { Some(dest) })
|
||||||
|
.into_iter()
|
||||||
|
.chain(labels.iter().copied().map(Some))
|
||||||
|
{
|
||||||
if let Some(block) = block {
|
if let Some(block) = block {
|
||||||
self.switch_to_block(block);
|
self.switch_to_block(block);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,11 +43,21 @@ pub unsafe fn asm_goto_with_outputs_use_in_label() -> u64 {
|
||||||
// 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 {
|
||||||
let out: u64;
|
|
||||||
// CHECK: callbr void asm sideeffect alignstack inteldialect "
|
// CHECK: callbr void asm sideeffect alignstack inteldialect "
|
||||||
// CHECK-NEXT: to label %unreachable [label %[[JUMPBB:[a-b0-9]+]]]
|
// CHECK-NEXT: to label %unreachable [label %[[JUMPBB:[a-b0-9]+]]]
|
||||||
asm!("jmp {}", label { return 1; }, options(noreturn));
|
asm!("jmp {}", label { return 1; }, options(noreturn));
|
||||||
// CHECK: [[JUMPBB]]:
|
// CHECK: [[JUMPBB]]:
|
||||||
// CHECK-NEXT: ret i64 1
|
// CHECK-NEXT: ret i64 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @asm_goto_noreturn_with_outputs
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn asm_goto_noreturn_with_outputs() -> 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!("mov {}, 1", "jmp {}", out(reg) out, label { return out; });
|
||||||
|
// CHECK: [[JUMPBB]]:
|
||||||
|
// CHECK-NEXT: ret i64 [[RES]]
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,25 @@ fn goto_out_jump() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn goto_out_jump_noreturn() {
|
||||||
|
unsafe {
|
||||||
|
let mut value = false;
|
||||||
|
let mut out: usize;
|
||||||
|
asm!(
|
||||||
|
"lea {}, [{} + 1]",
|
||||||
|
"jmp {}",
|
||||||
|
out(reg) out,
|
||||||
|
in(reg) 0x12345678usize,
|
||||||
|
label {
|
||||||
|
value = true;
|
||||||
|
assert_eq!(out, 0x12345679);
|
||||||
|
},
|
||||||
|
options(noreturn)
|
||||||
|
);
|
||||||
|
assert!(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// asm goto with outputs cause miscompilation in LLVM when multiple outputs are present.
|
// 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
|
// The code sample below is adapted from https://github.com/llvm/llvm-project/issues/74483
|
||||||
// and does not work with `-C opt-level=0`
|
// and does not work with `-C opt-level=0`
|
||||||
|
@ -131,6 +150,7 @@ fn main() {
|
||||||
goto_jump();
|
goto_jump();
|
||||||
goto_out_fallthrough();
|
goto_out_fallthrough();
|
||||||
goto_out_jump();
|
goto_out_jump();
|
||||||
|
goto_out_jump_noreturn();
|
||||||
// goto_multi_out();
|
// 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:124:9
|
--> $DIR/goto.rs:143: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:114:8
|
--> $DIR/goto.rs:133:8
|
||||||
|
|
|
|
||||||
LL | #[warn(unreachable_code)]
|
LL | #[warn(unreachable_code)]
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue