Add asm! to MIR

This commit is contained in:
Amanieu d'Antras 2020-02-14 18:17:50 +00:00
parent 10510b5820
commit 1e7b246086
29 changed files with 544 additions and 22 deletions

View File

@ -28,6 +28,7 @@ use rustc_macros::HashStable;
use rustc_serialize::{Decodable, Encodable};
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::asm::{InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece};
use std::borrow::Cow;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::ops::{Index, IndexMut};
@ -1178,6 +1179,23 @@ pub enum TerminatorKind<'tcx> {
/// of the `remove_noop_landing_pads` and `no_landing_pads` passes.
unwind: Option<BasicBlock>,
},
/// Block ends with an inline assembly block. This is a terminator since
/// inline assembly is allowed to diverge.
InlineAsm {
/// The template for the inline assembly, with placeholders.
template: &'tcx [InlineAsmTemplatePiece],
/// The operands for the inline assembly, as `Operand`s or `Place`s.
operands: Vec<InlineAsmOperand<'tcx>>,
/// Miscellaneous options for the inline assembly.
options: InlineAsmOptions,
/// Destination block after the inline assembly returns, unless it is
/// diverging (InlineAsmOptions::NORETURN).
destination: Option<BasicBlock>,
},
}
/// Information about an assertion failure.
@ -1192,6 +1210,34 @@ pub enum AssertKind<O> {
ResumedAfterPanic(GeneratorKind),
}
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
pub enum InlineAsmOperand<'tcx> {
In {
reg: InlineAsmRegOrRegClass,
value: Operand<'tcx>,
},
Out {
reg: InlineAsmRegOrRegClass,
late: bool,
place: Option<Place<'tcx>>,
},
InOut {
reg: InlineAsmRegOrRegClass,
late: bool,
in_value: Operand<'tcx>,
out_place: Option<Place<'tcx>>,
},
Const {
value: Operand<'tcx>,
},
SymFn {
value: Box<Constant<'tcx>>,
},
SymStatic {
value: Box<Constant<'tcx>>,
},
}
/// Type for MIR `Assert` terminator error messages.
pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
@ -1242,7 +1288,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| GeneratorDrop
| Return
| Unreachable
| Call { destination: None, cleanup: None, .. } => None.into_iter().chain(&[]),
| Call { destination: None, cleanup: None, .. }
| InlineAsm { destination: None, .. } => None.into_iter().chain(&[]),
Goto { target: ref t }
| Call { destination: None, cleanup: Some(ref t), .. }
| Call { destination: Some((_, ref t)), cleanup: None, .. }
@ -1250,7 +1297,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| DropAndReplace { target: ref t, unwind: None, .. }
| Drop { target: ref t, unwind: None, .. }
| Assert { target: ref t, cleanup: None, .. }
| FalseUnwind { real_target: ref t, unwind: None } => Some(t).into_iter().chain(&[]),
| FalseUnwind { real_target: ref t, unwind: None }
| InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]),
Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
| Yield { resume: ref t, drop: Some(ref u), .. }
| DropAndReplace { target: ref t, unwind: Some(ref u), .. }
@ -1274,7 +1322,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| GeneratorDrop
| Return
| Unreachable
| Call { destination: None, cleanup: None, .. } => None.into_iter().chain(&mut []),
| Call { destination: None, cleanup: None, .. }
| InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []),
Goto { target: ref mut t }
| Call { destination: None, cleanup: Some(ref mut t), .. }
| Call { destination: Some((_, ref mut t)), cleanup: None, .. }
@ -1282,9 +1331,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| DropAndReplace { target: ref mut t, unwind: None, .. }
| Drop { target: ref mut t, unwind: None, .. }
| Assert { target: ref mut t, cleanup: None, .. }
| FalseUnwind { real_target: ref mut t, unwind: None } => {
Some(t).into_iter().chain(&mut [])
}
| FalseUnwind { real_target: ref mut t, unwind: None }
| InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []),
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. }
| Yield { resume: ref mut t, drop: Some(ref mut u), .. }
| DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
@ -1310,7 +1358,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdges { .. } => None,
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::InlineAsm { .. } => None,
TerminatorKind::Call { cleanup: ref unwind, .. }
| TerminatorKind::Assert { cleanup: ref unwind, .. }
| TerminatorKind::DropAndReplace { ref unwind, .. }
@ -1329,7 +1378,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdges { .. } => None,
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::InlineAsm { .. } => None,
TerminatorKind::Call { cleanup: ref mut unwind, .. }
| TerminatorKind::Assert { cleanup: ref mut unwind, .. }
| TerminatorKind::DropAndReplace { ref mut unwind, .. }
@ -1544,6 +1594,50 @@ impl<'tcx> TerminatorKind<'tcx> {
}
FalseEdges { .. } => write!(fmt, "falseEdges"),
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
InlineAsm { template, ref operands, options, destination: _ } => {
write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
for op in operands {
write!(fmt, ", ")?;
let print_late = |&late| if late { "late" } else { "" };
match op {
InlineAsmOperand::In { reg, value } => {
write!(fmt, "in({}) {:?}", reg, value)?;
}
InlineAsmOperand::Out { reg, late, place: Some(place) } => {
write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
}
InlineAsmOperand::Out { reg, late, place: None } => {
write!(fmt, "{}out({}) _", print_late(late), reg)?;
}
InlineAsmOperand::InOut {
reg,
late,
in_value,
out_place: Some(out_place),
} => {
write!(
fmt,
"in{}out({}) {:?} => {:?}",
print_late(late),
reg,
in_value,
out_place
)?;
}
InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
}
InlineAsmOperand::Const { value } => {
write!(fmt, "const {:?}", value)?;
}
InlineAsmOperand::SymFn { value }
| InlineAsmOperand::SymStatic { value } => {
write!(fmt, "sym {:?}", value)?;
}
}
}
write!(fmt, ", options({:?}))", options)
}
}
}
@ -1586,6 +1680,8 @@ impl<'tcx> TerminatorKind<'tcx> {
FalseEdges { .. } => vec!["real".into(), "imaginary".into()],
FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
FalseUnwind { unwind: None, .. } => vec!["real".into()],
InlineAsm { destination: Some(_), .. } => vec!["".into()],
InlineAsm { destination: None, .. } => vec![],
}
}
}

View File

@ -78,6 +78,9 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
FalseEdges { real_target, imaginary_target }
}
FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
InlineAsm { template, ref operands, options, destination } => {
InlineAsm { template, operands: operands.fold_with(folder), options, destination }
}
};
Terminator { source_info: self.source_info, kind }
}
@ -120,6 +123,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
false
}
}
InlineAsm { ref operands, .. } => operands.visit_with(visitor),
Goto { .. }
| Resume
| Abort

View File

@ -531,6 +531,44 @@ macro_rules! make_mir_visitor {
);
}
TerminatorKind::InlineAsm {
template: _,
operands,
options: _,
destination: _,
} => {
for op in operands {
match op {
InlineAsmOperand::In { value, .. }
| InlineAsmOperand::Const { value } => {
self.visit_operand(value, source_location);
}
InlineAsmOperand::Out { place, .. } => {
if let Some(place) = place {
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
source_location,
);
}
}
InlineAsmOperand::InOut { in_value, out_place, .. } => {
self.visit_operand(in_value, source_location);
if let Some(out_place) = out_place {
self.visit_place(
out_place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
source_location,
);
}
}
InlineAsmOperand::SymFn { value }
| InlineAsmOperand::SymStatic { value } => {
self.visit_constant(value, source_location);
}
}
}
}
}
}

View File

@ -267,6 +267,9 @@ CloneTypeFoldableAndLiftImpls! {
::rustc_hir::MatchSource,
::rustc_hir::Mutability,
::rustc_hir::Unsafety,
::rustc_target::asm::InlineAsmOptions,
::rustc_target::asm::InlineAsmRegOrRegClass,
::rustc_target::asm::InlineAsmTemplatePiece,
::rustc_target::spec::abi::Abi,
crate::mir::Local,
crate::mir::Promoted,

View File

@ -1,8 +1,8 @@
use rustc_data_structures::graph::dominators::Dominators;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::TerminatorKind;
use rustc_middle::mir::{BasicBlock, Body, Location, Place, Rvalue};
use rustc_middle::mir::{BorrowKind, Mutability, Operand};
use rustc_middle::mir::{InlineAsmOperand, TerminatorKind};
use rustc_middle::mir::{Statement, StatementKind};
use rustc_middle::ty::TyCtxt;
@ -183,6 +183,29 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
}
}
}
TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => {
for op in operands {
match *op {
InlineAsmOperand::In { reg: _, ref value }
| InlineAsmOperand::Const { ref value } => {
self.consume_operand(location, value);
}
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
if let Some(place) = place {
self.mutate_place(location, place, Shallow(None), JustWrite);
}
}
InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
self.consume_operand(location, in_value);
if let Some(out_place) = out_place {
self.mutate_place(location, out_place, Shallow(None), JustWrite);
}
}
InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { value: _ } => {}
}
}
}
TerminatorKind::Goto { target: _ }
| TerminatorKind::Abort
| TerminatorKind::Unreachable

View File

@ -17,7 +17,7 @@ use rustc_middle::mir::{
};
use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
use rustc_middle::mir::{Terminator, TerminatorKind};
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, RegionVid, TyCtxt};
use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
@ -724,6 +724,42 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc
self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state);
}
TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => {
for op in operands {
match *op {
InlineAsmOperand::In { reg: _, ref value }
| InlineAsmOperand::Const { ref value } => {
self.consume_operand(loc, (value, span), flow_state);
}
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
if let Some(place) = place {
self.mutate_place(
loc,
(place, span),
Shallow(None),
JustWrite,
flow_state,
);
}
}
InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
self.consume_operand(loc, (in_value, span), flow_state);
if let Some(out_place) = out_place {
self.mutate_place(
loc,
(out_place, span),
Shallow(None),
JustWrite,
flow_state,
);
}
}
InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { value: _ } => {}
}
}
}
TerminatorKind::Goto { target: _ }
| TerminatorKind::Abort
| TerminatorKind::Unreachable
@ -778,7 +814,8 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ }
| TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Unreachable => {}
| TerminatorKind::Unreachable
| TerminatorKind::InlineAsm { .. } => {}
}
}
}

View File

@ -1548,7 +1548,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::FalseUnwind { .. } => {
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. } => {
// no checks needed for these
}
@ -1855,6 +1856,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.assert_iscleanup(body, block_data, unwind, true);
}
}
TerminatorKind::InlineAsm { ref destination, .. } => {
if let &Some(target) = destination {
self.assert_iscleanup(body, block_data, target, is_cleanup);
}
}
}
}

View File

@ -482,6 +482,12 @@ impl Direction for Forward {
}
}
InlineAsm { template: _, operands: _, options: _, destination } => {
if let Some(target) = destination {
propagate(target, exit_state);
}
}
SwitchInt { ref targets, ref values, ref discr, switch_ty: _ } => {
let enum_ = discr
.place()

View File

@ -203,6 +203,7 @@ where
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::Goto { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::Resume
| TerminatorKind::Return
| TerminatorKind::SwitchInt { .. }

View File

@ -317,10 +317,19 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
fn terminator_effect(
&self,
_: &mut impl GenKill<Self::Idx>,
_: &mir::Terminator<'tcx>,
_: Location,
trans: &mut impl GenKill<Self::Idx>,
teminator: &mir::Terminator<'tcx>,
_location: Location,
) {
if let mir::TerminatorKind::InlineAsm { operands, .. } = &teminator.kind {
for op in operands {
if let mir::InlineAsmOperand::Out { place: Some(place), .. }
| mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
{
self.kill_borrows_on_place(trans, place);
}
}
}
}
fn call_return_effect(

View File

@ -183,6 +183,23 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir,
// place to have storage *before* the yield, only after.
TerminatorKind::Yield { .. } => {}
TerminatorKind::InlineAsm { operands, .. } => {
for op in operands {
match op {
InlineAsmOperand::Out { place, .. }
| InlineAsmOperand::InOut { out_place: place, .. } => {
if let Some(place) = place {
trans.gen(place.local);
}
}
InlineAsmOperand::In { .. }
| InlineAsmOperand::Const { .. }
| InlineAsmOperand::SymFn { .. }
| InlineAsmOperand::SymStatic { .. } => {}
}
}
}
// Nothing to do for these. Match exhaustively so this fails to compile when new
// variants are added.
TerminatorKind::Call { destination: None, .. }
@ -228,6 +245,7 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir,
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::Goto { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::Resume
| TerminatorKind::Return
| TerminatorKind::SwitchInt { .. }

View File

@ -411,6 +411,31 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly);
}
}
TerminatorKind::InlineAsm { template: _, ref operands, options: _, destination: _ } => {
for op in operands {
match *op {
InlineAsmOperand::In { reg: _, ref value }
| InlineAsmOperand::Const { ref value } => {
self.gather_operand(value);
}
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
if let Some(place) = place {
self.create_move_path(place);
self.gather_init(place.as_ref(), InitKind::Deep);
}
}
InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
self.gather_operand(in_value);
if let Some(out_place) = out_place {
self.create_move_path(out_place);
self.gather_init(out_place.as_ref(), InitKind::Deep);
}
}
InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { value: _ } => {}
}
}
}
}
}

View File

@ -131,6 +131,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
"{:#?} should have been eliminated by MIR pass",
terminator.kind
),
// Inline assembly can't be interpreted.
InlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"),
}
Ok(())

View File

@ -639,7 +639,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
| mir::TerminatorKind::Abort
| mir::TerminatorKind::Return
| mir::TerminatorKind::Unreachable
| mir::TerminatorKind::Assert { .. } => {}
| mir::TerminatorKind::Assert { .. }
| mir::TerminatorKind::InlineAsm { .. } => {}
mir::TerminatorKind::GeneratorDrop
| mir::TerminatorKind::Yield { .. }
| mir::TerminatorKind::FalseEdges { .. }

View File

@ -603,6 +603,10 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
}
}
TerminatorKind::InlineAsm { .. } => {
self.check_op(ops::InlineAsm);
}
// FIXME: Some of these are only caught by `min_const_fn`, but should error here
// instead.
TerminatorKind::Abort

View File

@ -95,6 +95,12 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
self.check_target_features(func_id);
}
}
TerminatorKind::InlineAsm { .. } => self.require_unsafe(
"use of inline assembly",
"inline assembly is entirely unchecked and can cause undefined behavior",
UnsafetyViolationKind::General,
),
}
self.super_terminator(terminator, location);
}

View File

@ -1014,7 +1014,8 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::FalseUnwind { .. } => {}
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. } => {}
// Every argument in our function calls can be const propagated.
TerminatorKind::Call { ref mut args, .. } => {
let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level;

View File

@ -981,7 +981,8 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::FalseUnwind { .. } => {}
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. } => {}
// Resume will *continue* unwinding, but if there's no other unwinding terminator it
// will never be reached.

View File

@ -800,6 +800,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
{
bug!("False unwinds should have been removed before inlining")
}
TerminatorKind::InlineAsm { ref mut destination, .. } => {
if let Some(ref mut tgt) = *destination {
*tgt = self.update_target(*tgt);
}
}
}
}

View File

@ -391,5 +391,9 @@ fn check_terminator(
TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => {
check_operand(tcx, cond, span, def_id, body)
}
TerminatorKind::InlineAsm { .. } => {
Err((span, "cannot use inline assembly in const fn".into()))
}
}
}

View File

@ -77,7 +77,8 @@ impl RemoveNoopLandingPads {
| TerminatorKind::Call { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::Drop { .. } => false,
| TerminatorKind::Drop { .. }
| TerminatorKind::InlineAsm { .. } => false,
}
}

View File

@ -255,6 +255,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Return { .. }
| ExprKind::Literal { .. }
| ExprKind::StaticRef { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::LlvmInlineAsm { .. }
| ExprKind::Yield { .. }
| ExprKind::Call { .. } => {

View File

@ -252,6 +252,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::Return { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::LlvmInlineAsm { .. }
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. } => {

View File

@ -51,7 +51,8 @@ impl Category {
| ExprKind::Borrow { .. }
| ExprKind::AddressOf { .. }
| ExprKind::Yield { .. }
| ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
| ExprKind::Call { .. }
| ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Array { .. }
| ExprKind::Tuple { .. }

View File

@ -8,6 +8,7 @@ use rustc_hir as hir;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation};
use rustc_span::symbol::sym;
use rustc_target::asm::InlineAsmOptions;
use rustc_target::spec::abi::Abi;
@ -53,7 +54,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::NeverToAny { source } => {
let source = this.hir.mirror(source);
let is_call = match source.kind {
ExprKind::Call { .. } => true,
ExprKind::Call { .. } | ExprKind::InlineAsm { .. } => true,
_ => false,
};
@ -309,6 +310,73 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
block.unit()
}
ExprKind::InlineAsm { template, operands, options } => {
use crate::hair;
use rustc_middle::mir;
let operands = operands
.into_iter()
.map(|op| match op {
hair::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
reg,
value: unpack!(block = this.as_local_operand(block, expr)),
},
hair::InlineAsmOperand::Out { reg, late, expr } => {
mir::InlineAsmOperand::Out {
reg,
late,
place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
}
}
hair::InlineAsmOperand::InOut { reg, late, expr } => {
let place = unpack!(block = this.as_place(block, expr));
mir::InlineAsmOperand::InOut {
reg,
late,
// This works because asm operands must be Copy
in_value: Operand::Copy(place),
out_place: Some(place),
}
}
hair::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
mir::InlineAsmOperand::InOut {
reg,
late,
in_value: unpack!(block = this.as_local_operand(block, in_expr)),
out_place: out_expr.map(|out_expr| {
unpack!(block = this.as_place(block, out_expr))
}),
}
}
hair::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const {
value: unpack!(block = this.as_local_operand(block, expr)),
},
hair::InlineAsmOperand::SymFn { expr } => {
mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) }
}
hair::InlineAsmOperand::SymStatic { expr } => {
mir::InlineAsmOperand::SymStatic { value: box this.as_constant(expr) }
}
})
.collect();
let destination = this.cfg.start_new_block();
this.cfg.terminate(
block,
source_info,
TerminatorKind::InlineAsm {
template,
operands,
options,
destination: if options.contains(InlineAsmOptions::NORETURN) {
None
} else {
Some(destination)
},
},
);
destination.unit()
}
// These cases don't actually need a destination
ExprKind::Assign { .. }

View File

@ -1388,7 +1388,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
| TerminatorKind::Unreachable
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdges { .. } => {
| TerminatorKind::FalseEdges { .. }
| TerminatorKind::InlineAsm { .. } => {
span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind)
}
}

View File

@ -400,6 +400,121 @@ fn make_mirror_unadjusted<'a, 'tcx>(
convert_path_expr(cx, expr, res)
}
hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm {
template: asm.template,
operands: asm
.operands
.iter()
.map(|op| {
match *op {
hir::InlineAsmOperand::In { reg, ref expr } => {
InlineAsmOperand::In { reg, expr: expr.to_ref() }
}
hir::InlineAsmOperand::Out { reg, late, ref expr } => {
InlineAsmOperand::Out {
reg,
late,
expr: expr.as_ref().map(|expr| expr.to_ref()),
}
}
hir::InlineAsmOperand::InOut { reg, late, ref expr } => {
InlineAsmOperand::InOut { reg, late, expr: expr.to_ref() }
}
hir::InlineAsmOperand::SplitInOut {
reg,
late,
ref in_expr,
ref out_expr,
} => InlineAsmOperand::SplitInOut {
reg,
late,
in_expr: in_expr.to_ref(),
out_expr: out_expr.as_ref().map(|expr| expr.to_ref()),
},
hir::InlineAsmOperand::Const { ref expr } => {
InlineAsmOperand::Const { expr: expr.to_ref() }
}
hir::InlineAsmOperand::Sym { ref expr } => {
let qpath = match expr.kind {
hir::ExprKind::Path(ref qpath) => qpath,
_ => span_bug!(
expr.span,
"asm `sym` operand should be a path, found {:?}",
expr.kind
),
};
let temp_lifetime =
cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let res = cx.tables().qpath_res(qpath, expr.hir_id);
let ty;
match res {
Res::Def(DefKind::Fn, _) | Res::Def(DefKind::AssocFn, _) => {
ty = cx.tables().node_type(expr.hir_id);
let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res);
InlineAsmOperand::SymFn {
expr: Expr {
ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::Literal {
literal: ty::Const::zero_sized(cx.tcx, ty),
user_ty,
},
}
.to_ref(),
}
}
Res::Def(DefKind::Static, id) => {
ty = cx.tcx.static_ptr_ty(id);
let ptr = cx.tcx.create_static_alloc(id);
InlineAsmOperand::SymStatic {
expr: Expr {
ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::StaticRef {
literal: ty::Const::from_scalar(
cx.tcx,
Scalar::Ptr(ptr.into()),
ty,
),
def_id: id,
},
}
.to_ref(),
}
}
_ => {
cx.tcx.sess.span_err(
expr.span,
"asm `sym` operand must point to a fn or static",
);
// Not a real fn, but we're not reaching codegen anyways...
ty = cx.tcx.types.err;
InlineAsmOperand::SymFn {
expr: Expr {
ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::Literal {
literal: ty::Const::zero_sized(cx.tcx, ty),
user_ty: None,
},
}
.to_ref(),
}
}
}
}
}
})
.collect(),
options: asm.options,
},
hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm {
asm: &asm.inner,
outputs: asm.outputs_exprs.to_ref(),

View File

@ -15,6 +15,7 @@ use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType};
use rustc_span::Span;
use rustc_target::abi::VariantIdx;
use rustc_target::asm::{InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece};
crate mod constant;
crate mod cx;
@ -277,6 +278,11 @@ crate enum ExprKind<'tcx> {
literal: &'tcx Const<'tcx>,
def_id: DefId,
},
InlineAsm {
template: &'tcx [InlineAsmTemplatePiece],
operands: Vec<InlineAsmOperand<'tcx>>,
options: InlineAsmOptions,
},
LlvmInlineAsm {
asm: &'tcx hir::LlvmInlineAsmInner,
outputs: Vec<ExprRef<'tcx>>,
@ -335,6 +341,39 @@ impl<'tcx> ExprRef<'tcx> {
}
}
#[derive(Clone, Debug)]
crate enum InlineAsmOperand<'tcx> {
In {
reg: InlineAsmRegOrRegClass,
expr: ExprRef<'tcx>,
},
Out {
reg: InlineAsmRegOrRegClass,
late: bool,
expr: Option<ExprRef<'tcx>>,
},
InOut {
reg: InlineAsmRegOrRegClass,
late: bool,
expr: ExprRef<'tcx>,
},
SplitInOut {
reg: InlineAsmRegOrRegClass,
late: bool,
in_expr: ExprRef<'tcx>,
out_expr: Option<ExprRef<'tcx>>,
},
Const {
expr: ExprRef<'tcx>,
},
SymFn {
expr: ExprRef<'tcx>,
},
SymStatic {
expr: ExprRef<'tcx>,
},
}
///////////////////////////////////////////////////////////////////////////
// The Mirror trait

View File

@ -114,6 +114,10 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
| TerminatorKind::Unreachable
| TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive),
// FIXME(Amanieu): I am not 100% sure about this, but it triggers
// a spurious warning otherwise.
TerminatorKind::InlineAsm { .. } => ControlFlow::Break(NonRecursive),
// These do not.
TerminatorKind::Assert { .. }
| TerminatorKind::Call { .. }