mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-30 02:33:55 +00:00
Add asm! to MIR
This commit is contained in:
parent
10510b5820
commit
1e7b246086
@ -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![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -203,6 +203,7 @@ where
|
||||
| TerminatorKind::FalseUnwind { .. }
|
||||
| TerminatorKind::GeneratorDrop
|
||||
| TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::InlineAsm { .. }
|
||||
| TerminatorKind::Resume
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::SwitchInt { .. }
|
||||
|
@ -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(
|
||||
|
@ -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 { .. }
|
||||
|
@ -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: _ } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
|
@ -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 { .. }
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,8 @@ impl RemoveNoopLandingPads {
|
||||
| TerminatorKind::Call { .. }
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::DropAndReplace { .. }
|
||||
| TerminatorKind::Drop { .. } => false,
|
||||
| TerminatorKind::Drop { .. }
|
||||
| TerminatorKind::InlineAsm { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,6 +255,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
| ExprKind::Return { .. }
|
||||
| ExprKind::Literal { .. }
|
||||
| ExprKind::StaticRef { .. }
|
||||
| ExprKind::InlineAsm { .. }
|
||||
| ExprKind::LlvmInlineAsm { .. }
|
||||
| ExprKind::Yield { .. }
|
||||
| ExprKind::Call { .. } => {
|
||||
|
@ -252,6 +252,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
| ExprKind::Break { .. }
|
||||
| ExprKind::Continue { .. }
|
||||
| ExprKind::Return { .. }
|
||||
| ExprKind::InlineAsm { .. }
|
||||
| ExprKind::LlvmInlineAsm { .. }
|
||||
| ExprKind::PlaceTypeAscription { .. }
|
||||
| ExprKind::ValueTypeAscription { .. } => {
|
||||
|
@ -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 { .. }
|
||||
|
@ -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 { .. }
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 { .. }
|
||||
|
Loading…
Reference in New Issue
Block a user