1
Fork 0

LLVM codgen support for unwinding inline assembly

This commit is contained in:
cynecx 2021-09-04 19:25:09 +02:00
parent 491dd1f387
commit 91021de1f6
7 changed files with 109 additions and 18 deletions

View file

@ -239,7 +239,8 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
fx.add_comment(inst, terminator_head); fx.add_comment(inst, terminator_head);
} }
fx.set_debug_loc(bb_data.terminator().source_info); let source_info = bb_data.terminator().source_info;
fx.set_debug_loc(source_info);
match &bb_data.terminator().kind { match &bb_data.terminator().kind {
TerminatorKind::Goto { target } => { TerminatorKind::Goto { target } => {
@ -295,19 +296,19 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
let len = codegen_operand(fx, len).load_scalar(fx); let len = codegen_operand(fx, len).load_scalar(fx);
let index = codegen_operand(fx, index).load_scalar(fx); let index = codegen_operand(fx, index).load_scalar(fx);
let location = fx let location = fx
.get_caller_location(bb_data.terminator().source_info.span) .get_caller_location(source_info.span)
.load_scalar(fx); .load_scalar(fx);
codegen_panic_inner( codegen_panic_inner(
fx, fx,
rustc_hir::LangItem::PanicBoundsCheck, rustc_hir::LangItem::PanicBoundsCheck,
&[index, len, location], &[index, len, location],
bb_data.terminator().source_info.span, source_info.span,
); );
} }
_ => { _ => {
let msg_str = msg.description(); let msg_str = msg.description();
codegen_panic(fx, msg_str, bb_data.terminator().source_info.span); codegen_panic(fx, msg_str, source_info.span);
} }
} }
} }
@ -378,10 +379,18 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
options, options,
destination, destination,
line_spans: _, line_spans: _,
cleanup,
} => { } => {
if cleanup.is_some() {
fx.tcx.sess.span_fatal(
source_info.span,
"cranelift doesn't support unwinding from inline assembly.",
);
}
crate::inline_asm::codegen_inline_asm( crate::inline_asm::codegen_inline_asm(
fx, fx,
bb_data.terminator().source_info.span, source_info.span,
template, template,
operands, operands,
*options, *options,
@ -415,7 +424,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
} }
TerminatorKind::Drop { place, target, unwind: _ } => { TerminatorKind::Drop { place, target, unwind: _ } => {
let drop_place = codegen_place(fx, *place); let drop_place = codegen_place(fx, *place);
crate::abi::codegen_drop(fx, bb_data.terminator().source_info.span, drop_place); crate::abi::codegen_drop(fx, source_info.span, drop_place);
let target_block = fx.get_block(*target); let target_block = fx.get_block(*target);
fx.bcx.ins().jump(target_block, &[]); fx.bcx.ins().jump(target_block, &[]);

View file

@ -1,6 +1,8 @@
use crate::builder::Builder; use crate::builder::Builder;
use crate::common::Funclet;
use crate::context::CodegenCx; use crate::context::CodegenCx;
use crate::llvm; use crate::llvm;
use crate::llvm_util;
use crate::type_::Type; use crate::type_::Type;
use crate::type_of::LayoutLlvmExt; use crate::type_of::LayoutLlvmExt;
use crate::value::Value; use crate::value::Value;
@ -98,6 +100,8 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
ia.alignstack, ia.alignstack,
ia.dialect, ia.dialect,
&[span], &[span],
false,
None,
); );
if r.is_none() { if r.is_none() {
return false; return false;
@ -121,6 +125,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
options: InlineAsmOptions, options: InlineAsmOptions,
line_spans: &[Span], line_spans: &[Span],
instance: Instance<'_>, instance: Instance<'_>,
dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>,
) { ) {
let asm_arch = self.tcx.sess.asm_arch.unwrap(); let asm_arch = self.tcx.sess.asm_arch.unwrap();
@ -355,6 +360,8 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
alignstack, alignstack,
dialect, dialect,
line_spans, line_spans,
options.contains(InlineAsmOptions::MAY_UNWIND),
dest_catch_funclet,
) )
.unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed")); .unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed"));
@ -447,10 +454,16 @@ pub(crate) fn inline_asm_call(
alignstack: bool, alignstack: bool,
dia: LlvmAsmDialect, dia: LlvmAsmDialect,
line_spans: &[Span], line_spans: &[Span],
unwind: bool,
dest_catch_funclet: Option<(
&'ll llvm::BasicBlock,
&'ll llvm::BasicBlock,
Option<&Funclet<'ll>>,
)>,
) -> Option<&'ll Value> { ) -> Option<&'ll Value> {
let volatile = if volatile { llvm::True } else { llvm::False }; let volatile = if volatile { llvm::True } else { llvm::False };
let alignstack = if alignstack { llvm::True } else { llvm::False }; let alignstack = if alignstack { llvm::True } else { llvm::False };
let can_throw = llvm::False; let can_throw = if unwind { llvm::True } else { llvm::False };
let argtys = inputs let argtys = inputs
.iter() .iter()
@ -467,6 +480,13 @@ pub(crate) fn inline_asm_call(
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr().cast(), cons.len()); let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr().cast(), cons.len());
debug!("constraint verification result: {:?}", constraints_ok); debug!("constraint verification result: {:?}", constraints_ok);
if constraints_ok { if constraints_ok {
if unwind && llvm_util::get_version() < (13, 0, 0) {
bx.cx.sess().span_fatal(
line_spans[0],
"unwinding from inline assembly is only supported on llvm >= 13.",
);
}
let v = llvm::LLVMRustInlineAsm( let v = llvm::LLVMRustInlineAsm(
fty, fty,
asm.as_ptr().cast(), asm.as_ptr().cast(),
@ -478,7 +498,12 @@ pub(crate) fn inline_asm_call(
llvm::AsmDialect::from_generic(dia), llvm::AsmDialect::from_generic(dia),
can_throw, can_throw,
); );
let call = bx.call(fty, v, inputs, None);
let call = if let Some((dest, catch, funclet)) = dest_catch_funclet {
bx.invoke(fty, v, inputs, dest, catch, funclet)
} else {
bx.call(fty, v, inputs, None)
};
// Store mark in a metadata node so we can map LLVM errors // Store mark in a metadata node so we can map LLVM errors
// back to source locations. See #17552. // back to source locations. See #17552.

View file

@ -350,6 +350,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
false, false,
ast::LlvmAsmDialect::Att, ast::LlvmAsmDialect::Att,
&[span], &[span],
false,
None,
) )
.unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`")); .unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`"));

View file

@ -276,9 +276,9 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
| TerminatorKind::SwitchInt { .. } | TerminatorKind::SwitchInt { .. }
| TerminatorKind::Yield { .. } | TerminatorKind::Yield { .. }
| TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } | TerminatorKind::FalseUnwind { .. } => { /* nothing to do */ }
| TerminatorKind::InlineAsm { .. } => { /* nothing to do */ }
TerminatorKind::Call { cleanup: unwind, .. } TerminatorKind::Call { cleanup: unwind, .. }
| TerminatorKind::InlineAsm { cleanup: unwind, .. }
| TerminatorKind::Assert { cleanup: unwind, .. } | TerminatorKind::Assert { cleanup: unwind, .. }
| TerminatorKind::DropAndReplace { unwind, .. } | TerminatorKind::DropAndReplace { unwind, .. }
| TerminatorKind::Drop { unwind, .. } => { | TerminatorKind::Drop { unwind, .. } => {

View file

@ -10,6 +10,7 @@ use crate::traits::*;
use crate::MemFlags; use crate::MemFlags;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
use rustc_middle::mir::AssertKind; use rustc_middle::mir::AssertKind;
@ -174,6 +175,45 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
} }
} }
} }
/// Generates inline assembly with optional `destination` and `cleanup`.
fn do_inlineasm<Bx: BuilderMethods<'a, 'tcx>>(
&self,
fx: &mut FunctionCx<'a, 'tcx, Bx>,
bx: &mut Bx,
template: &[InlineAsmTemplatePiece],
operands: &[InlineAsmOperandRef<'tcx, Bx>],
options: InlineAsmOptions,
line_spans: &[Span],
destination: Option<mir::BasicBlock>,
cleanup: Option<mir::BasicBlock>,
instance: Instance<'_>,
) {
if let Some(cleanup) = cleanup {
let ret_llbb = if let Some(target) = destination {
fx.llbb(target)
} else {
fx.unreachable_block()
};
bx.codegen_inline_asm(
template,
&operands,
options,
line_spans,
instance,
Some((ret_llbb, self.llblock(fx, cleanup), self.funclet(fx))),
);
} else {
bx.codegen_inline_asm(template, &operands, options, line_spans, instance, None);
if let Some(target) = destination {
self.funclet_br(fx, bx, target);
} else {
bx.unreachable();
}
}
}
} }
/// Codegen implementations for some terminator variants. /// Codegen implementations for some terminator variants.
@ -877,6 +917,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options: ast::InlineAsmOptions, options: ast::InlineAsmOptions,
line_spans: &[Span], line_spans: &[Span],
destination: Option<mir::BasicBlock>, destination: Option<mir::BasicBlock>,
cleanup: Option<mir::BasicBlock>,
instance: Instance<'_>, instance: Instance<'_>,
) { ) {
let span = terminator.source_info.span; let span = terminator.source_info.span;
@ -931,13 +972,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}) })
.collect(); .collect();
bx.codegen_inline_asm(template, &operands, options, line_spans, instance); helper.do_inlineasm(
self,
if let Some(target) = destination { &mut bx,
helper.funclet_br(self, &mut bx, target); template,
} else { &operands,
bx.unreachable(); options,
} line_spans,
destination,
cleanup,
instance,
);
} }
} }
@ -1041,7 +1086,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options, options,
line_spans, line_spans,
destination, destination,
cleanup: _, // TODO cleanup,
} => { } => {
self.codegen_asm_terminator( self.codegen_asm_terminator(
helper, helper,
@ -1052,6 +1097,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options, options,
line_spans, line_spans,
destination, destination,
cleanup,
self.instance, self.instance,
); );
} }

View file

@ -59,6 +59,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes {
options: InlineAsmOptions, options: InlineAsmOptions,
line_spans: &[Span], line_spans: &[Span],
instance: Instance<'_>, instance: Instance<'_>,
dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>,
); );
} }

View file

@ -446,11 +446,19 @@ LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
char *Constraints, size_t ConstraintsLen, char *Constraints, size_t ConstraintsLen,
LLVMBool HasSideEffects, LLVMBool IsAlignStack, LLVMBool HasSideEffects, LLVMBool IsAlignStack,
LLVMRustAsmDialect Dialect, LLVMBool CanThrow) { LLVMRustAsmDialect Dialect, LLVMBool CanThrow) {
#if LLVM_VERSION_GE(13, 0)
return wrap(InlineAsm::get(unwrap<FunctionType>(Ty), return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
StringRef(AsmString, AsmStringLen), StringRef(AsmString, AsmStringLen),
StringRef(Constraints, ConstraintsLen), StringRef(Constraints, ConstraintsLen),
HasSideEffects, IsAlignStack, HasSideEffects, IsAlignStack,
fromRust(Dialect), CanThrow)); fromRust(Dialect), CanThrow));
#else
return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
StringRef(AsmString, AsmStringLen),
StringRef(Constraints, ConstraintsLen),
HasSideEffects, IsAlignStack,
fromRust(Dialect)));
#endif
} }
extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,