mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
Lower asm expressions
This commit is contained in:
parent
86658c66b4
commit
3b11ff8c4d
14 changed files with 612 additions and 97 deletions
|
@ -1,6 +1,8 @@
|
||||||
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
|
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
|
||||||
//! representation.
|
//! representation.
|
||||||
|
|
||||||
|
mod asm;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
|
@ -35,8 +37,8 @@ use crate::{
|
||||||
FormatPlaceholder, FormatSign, FormatTrait,
|
FormatPlaceholder, FormatSign, FormatTrait,
|
||||||
},
|
},
|
||||||
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
||||||
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
|
Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
|
||||||
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
PatId, RecordFieldPat, RecordLitField, Statement,
|
||||||
},
|
},
|
||||||
item_scope::BuiltinShadowMode,
|
item_scope::BuiltinShadowMode,
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
|
@ -693,13 +695,7 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
|
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
|
||||||
ast::Expr::AsmExpr(e) => {
|
ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr),
|
||||||
let template = e.template().map(|it| self.collect_expr(it)).collect();
|
|
||||||
self.alloc_expr(
|
|
||||||
Expr::InlineAsm(InlineAsm { template, operands: Box::default() }),
|
|
||||||
syntax_ptr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ast::Expr::OffsetOfExpr(e) => {
|
ast::Expr::OffsetOfExpr(e) => {
|
||||||
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
|
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
|
||||||
let fields = e.fields().map(|it| it.as_name()).collect();
|
let fields = e.fields().map(|it| it.as_name()).collect();
|
||||||
|
@ -2064,6 +2060,7 @@ impl ExprCollector<'_> {
|
||||||
is_assignee_expr: false,
|
is_assignee_expr: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion: format
|
// endregion: format
|
||||||
|
|
||||||
fn lang_path(&self, lang: LangItem) -> Option<Path> {
|
fn lang_path(&self, lang: LangItem) -> Option<Path> {
|
||||||
|
|
230
crates/hir-def/src/body/lower/asm.rs
Normal file
230
crates/hir-def/src/body/lower/asm.rs
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
use hir_expand::name::Name;
|
||||||
|
use intern::Symbol;
|
||||||
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
use syntax::{
|
||||||
|
ast::{self, HasName, IsString},
|
||||||
|
AstNode, AstPtr, AstToken, T,
|
||||||
|
};
|
||||||
|
use tt::{TextRange, TextSize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
body::lower::{ExprCollector, FxIndexSet},
|
||||||
|
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl ExprCollector<'_> {
|
||||||
|
pub(super) fn lower_inline_asm(
|
||||||
|
&mut self,
|
||||||
|
asm: ast::AsmExpr,
|
||||||
|
syntax_ptr: AstPtr<ast::Expr>,
|
||||||
|
) -> ExprId {
|
||||||
|
let mut clobber_abis = FxIndexSet::default();
|
||||||
|
let mut operands = vec![];
|
||||||
|
let mut options = AsmOptions::empty();
|
||||||
|
|
||||||
|
let mut named_pos: FxHashMap<usize, Symbol> = Default::default();
|
||||||
|
let mut named_args: FxHashMap<Symbol, usize> = Default::default();
|
||||||
|
let mut reg_args: FxHashSet<usize> = Default::default();
|
||||||
|
for operand in asm.asm_operands() {
|
||||||
|
let slot = operands.len();
|
||||||
|
let mut lower_reg = |reg: Option<ast::AsmRegSpec>| {
|
||||||
|
let reg = reg?;
|
||||||
|
if let Some(string) = reg.string_token() {
|
||||||
|
reg_args.insert(slot);
|
||||||
|
Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text())))
|
||||||
|
} else {
|
||||||
|
reg.name_ref().map(|name_ref| {
|
||||||
|
InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let op = match operand {
|
||||||
|
ast::AsmOperand::AsmClobberAbi(clobber_abi) => {
|
||||||
|
if let Some(abi_name) = clobber_abi.string_token() {
|
||||||
|
clobber_abis.insert(Symbol::intern(abi_name.text()));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ast::AsmOperand::AsmOptions(opt) => {
|
||||||
|
opt.asm_options().for_each(|opt| {
|
||||||
|
options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) {
|
||||||
|
T![att_syntax] => AsmOptions::ATT_SYNTAX,
|
||||||
|
T![may_unwind] => AsmOptions::MAY_UNWIND,
|
||||||
|
T![nomem] => AsmOptions::NOMEM,
|
||||||
|
T![noreturn] => AsmOptions::NORETURN,
|
||||||
|
T![nostack] => AsmOptions::NOSTACK,
|
||||||
|
T![preserves_flags] => AsmOptions::PRESERVES_FLAGS,
|
||||||
|
T![pure] => AsmOptions::PURE,
|
||||||
|
T![raw] => AsmOptions::RAW,
|
||||||
|
T![readonly] => AsmOptions::READONLY,
|
||||||
|
_ => return,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ast::AsmOperand::AsmRegOperand(op) => {
|
||||||
|
let Some(dir_spec) = op.asm_dir_spec() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(reg) = lower_reg(op.asm_reg_spec()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(name) = op.name() {
|
||||||
|
let sym = Symbol::intern(&name.text());
|
||||||
|
named_args.insert(sym.clone(), slot);
|
||||||
|
named_pos.insert(slot, sym);
|
||||||
|
}
|
||||||
|
if dir_spec.in_token().is_some() {
|
||||||
|
let expr = self
|
||||||
|
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
|
||||||
|
AsmOperand::In { reg, expr }
|
||||||
|
} else if dir_spec.out_token().is_some() {
|
||||||
|
let expr = self
|
||||||
|
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
|
||||||
|
AsmOperand::Out { reg, expr: Some(expr), late: false }
|
||||||
|
} else if dir_spec.lateout_token().is_some() {
|
||||||
|
let expr = self
|
||||||
|
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
|
||||||
|
AsmOperand::Out { reg, expr: Some(expr), late: true }
|
||||||
|
} else if dir_spec.inout_token().is_some() {
|
||||||
|
let Some(op_expr) = op.asm_operand_expr() else { continue };
|
||||||
|
let in_expr = self.collect_expr_opt(op_expr.in_expr());
|
||||||
|
let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
|
||||||
|
match out_expr {
|
||||||
|
Some(out_expr) => AsmOperand::SplitInOut {
|
||||||
|
reg,
|
||||||
|
in_expr,
|
||||||
|
out_expr: Some(out_expr),
|
||||||
|
late: false,
|
||||||
|
},
|
||||||
|
None => AsmOperand::InOut { reg, expr: in_expr, late: false },
|
||||||
|
}
|
||||||
|
} else if dir_spec.inlateout_token().is_some() {
|
||||||
|
let Some(op_expr) = op.asm_operand_expr() else { continue };
|
||||||
|
let in_expr = self.collect_expr_opt(op_expr.in_expr());
|
||||||
|
let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
|
||||||
|
match out_expr {
|
||||||
|
Some(out_expr) => AsmOperand::SplitInOut {
|
||||||
|
reg,
|
||||||
|
in_expr,
|
||||||
|
out_expr: Some(out_expr),
|
||||||
|
late: false,
|
||||||
|
},
|
||||||
|
None => AsmOperand::InOut { reg, expr: in_expr, late: false },
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::AsmOperand::AsmLabel(l) => {
|
||||||
|
AsmOperand::Label(self.collect_block_opt(l.block_expr()))
|
||||||
|
}
|
||||||
|
ast::AsmOperand::AsmConst(c) => AsmOperand::Const(self.collect_expr_opt(c.expr())),
|
||||||
|
ast::AsmOperand::AsmSym(s) => {
|
||||||
|
let Some(path) = s.path().and_then(|p| self.expander.parse_path(self.db, p))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
AsmOperand::Sym(path)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
operands.push(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mappings = vec![];
|
||||||
|
let mut curarg = 0;
|
||||||
|
if !options.contains(AsmOptions::RAW) {
|
||||||
|
// Don't treat raw asm as a format string.
|
||||||
|
asm.template()
|
||||||
|
.filter_map(|it| Some((it.clone(), self.expand_macros_to_string(it)?)))
|
||||||
|
.for_each(|(expr, (s, is_direct_literal))| {
|
||||||
|
let Ok(text) = s.value() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let template_snippet = match expr {
|
||||||
|
ast::Expr::Literal(literal) => match literal.kind() {
|
||||||
|
ast::LiteralKind::String(s) => Some(s.text().to_owned()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let str_style = match s.quote_offsets() {
|
||||||
|
Some(offsets) => {
|
||||||
|
let raw = usize::from(offsets.quotes.0.len()) - 1;
|
||||||
|
// subtract 1 for the `r` prefix
|
||||||
|
(raw != 0).then(|| raw - 1)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parser = rustc_parse_format::Parser::new(
|
||||||
|
&text,
|
||||||
|
str_style,
|
||||||
|
template_snippet,
|
||||||
|
false,
|
||||||
|
rustc_parse_format::ParseMode::InlineAsm,
|
||||||
|
);
|
||||||
|
parser.curarg = curarg;
|
||||||
|
|
||||||
|
let mut unverified_pieces = Vec::new();
|
||||||
|
while let Some(piece) = parser.next() {
|
||||||
|
if !parser.errors.is_empty() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
unverified_pieces.push(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curarg = parser.curarg;
|
||||||
|
|
||||||
|
let to_span = |inner_span: rustc_parse_format::InnerSpan| {
|
||||||
|
is_direct_literal.then(|| {
|
||||||
|
TextRange::new(
|
||||||
|
inner_span.start.try_into().unwrap(),
|
||||||
|
inner_span.end.try_into().unwrap(),
|
||||||
|
) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
for piece in unverified_pieces {
|
||||||
|
match piece {
|
||||||
|
rustc_parse_format::Piece::String(_) => {}
|
||||||
|
rustc_parse_format::Piece::NextArgument(arg) => {
|
||||||
|
// let span = arg_spans.next();
|
||||||
|
|
||||||
|
let _operand_idx = match arg.position {
|
||||||
|
rustc_parse_format::ArgumentIs(idx)
|
||||||
|
| rustc_parse_format::ArgumentImplicitlyIs(idx) => {
|
||||||
|
if idx >= operands.len()
|
||||||
|
|| named_pos.contains_key(&idx)
|
||||||
|
|| reg_args.contains(&idx)
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rustc_parse_format::ArgumentNamed(name) => {
|
||||||
|
let name = Symbol::intern(name);
|
||||||
|
if let Some(position_span) = to_span(arg.position_span) {
|
||||||
|
mappings.push((
|
||||||
|
position_span,
|
||||||
|
Name::new_symbol_root(name.clone()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
named_args.get(&name).copied()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let idx = self.alloc_expr(
|
||||||
|
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
|
||||||
|
syntax_ptr,
|
||||||
|
);
|
||||||
|
self.source_map.format_args_template_map.insert(idx, mappings);
|
||||||
|
idx
|
||||||
|
}
|
||||||
|
}
|
|
@ -307,8 +307,120 @@ pub struct OffsetOf {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct InlineAsm {
|
pub struct InlineAsm {
|
||||||
pub template: Box<[ExprId]>,
|
pub operands: Box<[AsmOperand]>,
|
||||||
pub operands: Box<[()]>,
|
pub options: AsmOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct AsmOptions(u16);
|
||||||
|
bitflags::bitflags! {
|
||||||
|
impl AsmOptions: u16 {
|
||||||
|
const PURE = 1 << 0;
|
||||||
|
const NOMEM = 1 << 1;
|
||||||
|
const READONLY = 1 << 2;
|
||||||
|
const PRESERVES_FLAGS = 1 << 3;
|
||||||
|
const NORETURN = 1 << 4;
|
||||||
|
const NOSTACK = 1 << 5;
|
||||||
|
const ATT_SYNTAX = 1 << 6;
|
||||||
|
const RAW = 1 << 7;
|
||||||
|
const MAY_UNWIND = 1 << 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsmOptions {
|
||||||
|
pub const COUNT: usize = Self::all().bits().count_ones() as usize;
|
||||||
|
|
||||||
|
pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
|
||||||
|
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
|
||||||
|
|
||||||
|
pub fn human_readable_names(&self) -> Vec<&'static str> {
|
||||||
|
let mut options = vec![];
|
||||||
|
|
||||||
|
if self.contains(AsmOptions::PURE) {
|
||||||
|
options.push("pure");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::NOMEM) {
|
||||||
|
options.push("nomem");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::READONLY) {
|
||||||
|
options.push("readonly");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::PRESERVES_FLAGS) {
|
||||||
|
options.push("preserves_flags");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::NORETURN) {
|
||||||
|
options.push("noreturn");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::NOSTACK) {
|
||||||
|
options.push("nostack");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::ATT_SYNTAX) {
|
||||||
|
options.push("att_syntax");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::RAW) {
|
||||||
|
options.push("raw");
|
||||||
|
}
|
||||||
|
if self.contains(AsmOptions::MAY_UNWIND) {
|
||||||
|
options.push("may_unwind");
|
||||||
|
}
|
||||||
|
|
||||||
|
options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for AsmOptions {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
bitflags::parser::to_writer(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub enum AsmOperand {
|
||||||
|
In {
|
||||||
|
reg: InlineAsmRegOrRegClass,
|
||||||
|
expr: ExprId,
|
||||||
|
},
|
||||||
|
Out {
|
||||||
|
reg: InlineAsmRegOrRegClass,
|
||||||
|
expr: Option<ExprId>,
|
||||||
|
late: bool,
|
||||||
|
},
|
||||||
|
InOut {
|
||||||
|
reg: InlineAsmRegOrRegClass,
|
||||||
|
expr: ExprId,
|
||||||
|
late: bool,
|
||||||
|
},
|
||||||
|
SplitInOut {
|
||||||
|
reg: InlineAsmRegOrRegClass,
|
||||||
|
in_expr: ExprId,
|
||||||
|
out_expr: Option<ExprId>,
|
||||||
|
late: bool,
|
||||||
|
},
|
||||||
|
Label(ExprId),
|
||||||
|
Const(ExprId),
|
||||||
|
Sym(Path),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsmOperand {
|
||||||
|
pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
|
||||||
|
match self {
|
||||||
|
Self::In { reg, .. }
|
||||||
|
| Self::Out { reg, .. }
|
||||||
|
| Self::InOut { reg, .. }
|
||||||
|
| Self::SplitInOut { reg, .. } => Some(reg),
|
||||||
|
Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_clobber(&self) -> bool {
|
||||||
|
matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub enum InlineAsmRegOrRegClass {
|
||||||
|
Reg(Symbol),
|
||||||
|
RegClass(Symbol),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -373,7 +485,21 @@ impl Expr {
|
||||||
match self {
|
match self {
|
||||||
Expr::Missing => {}
|
Expr::Missing => {}
|
||||||
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
||||||
Expr::InlineAsm(it) => it.template.iter().copied().for_each(f),
|
Expr::InlineAsm(it) => it.operands.iter().for_each(|op| match op {
|
||||||
|
AsmOperand::In { expr, .. }
|
||||||
|
| AsmOperand::Out { expr: Some(expr), .. }
|
||||||
|
| AsmOperand::InOut { expr, .. } => f(*expr),
|
||||||
|
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||||
|
f(*in_expr);
|
||||||
|
if let Some(out_expr) = out_expr {
|
||||||
|
f(*out_expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AsmOperand::Out { expr: None, .. }
|
||||||
|
| AsmOperand::Const(_)
|
||||||
|
| AsmOperand::Label(_)
|
||||||
|
| AsmOperand::Sym(_) => (),
|
||||||
|
}),
|
||||||
Expr::If { condition, then_branch, else_branch } => {
|
Expr::If { condition, then_branch, else_branch } => {
|
||||||
f(*condition);
|
f(*condition);
|
||||||
f(*then_branch);
|
f(*then_branch);
|
||||||
|
|
|
@ -50,11 +50,7 @@ fn main() {
|
||||||
let i: u64 = 3;
|
let i: u64 = 3;
|
||||||
let o: u64;
|
let o: u64;
|
||||||
unsafe {
|
unsafe {
|
||||||
builtin #asm ( {
|
builtin #asm ("mov {0}, {1}", "add {0}, 5", out (reg)o, in (reg)i, );
|
||||||
$crate::format_args!("mov {0}, {1}");
|
|
||||||
$crate::format_args!("add {0}, 5");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"##]],
|
"##]],
|
||||||
|
|
|
@ -119,9 +119,8 @@ register_builtin! {
|
||||||
(module_path, ModulePath) => module_path_expand,
|
(module_path, ModulePath) => module_path_expand,
|
||||||
(assert, Assert) => assert_expand,
|
(assert, Assert) => assert_expand,
|
||||||
(stringify, Stringify) => stringify_expand,
|
(stringify, Stringify) => stringify_expand,
|
||||||
(llvm_asm, LlvmAsm) => asm_expand,
|
|
||||||
(asm, Asm) => asm_expand,
|
(asm, Asm) => asm_expand,
|
||||||
(global_asm, GlobalAsm) => global_asm_expand,
|
(global_asm, GlobalAsm) => asm_expand,
|
||||||
(cfg, Cfg) => cfg_expand,
|
(cfg, Cfg) => cfg_expand,
|
||||||
(core_panic, CorePanic) => panic_expand,
|
(core_panic, CorePanic) => panic_expand,
|
||||||
(std_panic, StdPanic) => panic_expand,
|
(std_panic, StdPanic) => panic_expand,
|
||||||
|
@ -324,40 +323,15 @@ fn asm_expand(
|
||||||
tt: &tt::Subtree,
|
tt: &tt::Subtree,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
// We expand all assembly snippets to `format_args!` invocations to get format syntax
|
let mut tt = tt.clone();
|
||||||
// highlighting for them.
|
tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
|
||||||
let mut literals = Vec::new();
|
|
||||||
for tt in tt.token_trees.chunks(2) {
|
|
||||||
match tt {
|
|
||||||
[tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
|
|
||||||
| [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] =>
|
|
||||||
{
|
|
||||||
let dollar_krate = dollar_crate(span);
|
|
||||||
literals.push(quote!(span=>#dollar_krate::format_args!(#lit);));
|
|
||||||
}
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pound = mk_pound(span);
|
let pound = mk_pound(span);
|
||||||
let expanded = quote! {span =>
|
let expanded = quote! {span =>
|
||||||
builtin #pound asm (
|
builtin #pound asm #tt
|
||||||
{##literals}
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
ExpandResult::ok(expanded)
|
ExpandResult::ok(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_asm_expand(
|
|
||||||
_db: &dyn ExpandDatabase,
|
|
||||||
_id: MacroCallId,
|
|
||||||
_tt: &tt::Subtree,
|
|
||||||
span: Span,
|
|
||||||
) -> ExpandResult<tt::Subtree> {
|
|
||||||
// Expand to nothing (at item-level)
|
|
||||||
ExpandResult::ok(quote! {span =>})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cfg_expand(
|
fn cfg_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
|
|
@ -10,7 +10,10 @@ use chalk_ir::{
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
data::adt::VariantData,
|
data::adt::VariantData,
|
||||||
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
|
hir::{
|
||||||
|
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement,
|
||||||
|
UnaryOp,
|
||||||
|
},
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||||
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
||||||
|
@ -666,9 +669,21 @@ impl InferenceContext<'_> {
|
||||||
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
|
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
|
||||||
match &self.body[tgt_expr] {
|
match &self.body[tgt_expr] {
|
||||||
Expr::OffsetOf(_) => (),
|
Expr::OffsetOf(_) => (),
|
||||||
Expr::InlineAsm(e) => {
|
Expr::InlineAsm(e) => e.operands.iter().for_each(|op| match op {
|
||||||
e.template.iter().for_each(|it| self.walk_expr_without_adjust(*it))
|
AsmOperand::In { expr, .. }
|
||||||
|
| AsmOperand::Out { expr: Some(expr), .. }
|
||||||
|
| AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr),
|
||||||
|
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||||
|
self.walk_expr_without_adjust(*in_expr);
|
||||||
|
if let Some(out_expr) = out_expr {
|
||||||
|
self.walk_expr_without_adjust(*out_expr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
AsmOperand::Out { expr: None, .. }
|
||||||
|
| AsmOperand::Const(_)
|
||||||
|
| AsmOperand::Label(_)
|
||||||
|
| AsmOperand::Sym(_) => (),
|
||||||
|
}),
|
||||||
Expr::If { condition, then_branch, else_branch } => {
|
Expr::If { condition, then_branch, else_branch } => {
|
||||||
self.consume_expr(*condition);
|
self.consume_expr(*condition);
|
||||||
self.consume_expr(*then_branch);
|
self.consume_expr(*then_branch);
|
||||||
|
|
|
@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
hir::{
|
hir::{
|
||||||
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
|
||||||
|
Literal, Statement, UnaryOp,
|
||||||
},
|
},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
path::{GenericArg, GenericArgs, Path},
|
path::{GenericArg, GenericArgs, Path},
|
||||||
|
@ -41,9 +42,9 @@ use crate::{
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig,
|
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
|
||||||
FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
|
FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
|
||||||
TyExt, TyKind,
|
TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -924,10 +925,62 @@ impl InferenceContext<'_> {
|
||||||
expected
|
expected
|
||||||
}
|
}
|
||||||
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
||||||
Expr::InlineAsm(it) => {
|
Expr::InlineAsm(asm) => {
|
||||||
it.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr));
|
let mut check_expr_asm_operand = |expr, is_input: bool| {
|
||||||
|
let ty = self.infer_expr_no_expect(expr);
|
||||||
|
|
||||||
|
// If this is an input value, we require its type to be fully resolved
|
||||||
|
// at this point. This allows us to provide helpful coercions which help
|
||||||
|
// pass the type candidate list in a later pass.
|
||||||
|
//
|
||||||
|
// We don't require output types to be resolved at this point, which
|
||||||
|
// allows them to be inferred based on how they are used later in the
|
||||||
|
// function.
|
||||||
|
if is_input {
|
||||||
|
let ty = self.resolve_ty_shallow(&ty);
|
||||||
|
match ty.kind(Interner) {
|
||||||
|
TyKind::FnDef(def, parameters) => {
|
||||||
|
let fnptr_ty = TyKind::Function(
|
||||||
|
CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
|
||||||
|
)
|
||||||
|
.intern(Interner);
|
||||||
|
_ = self.coerce(Some(expr), &ty, &fnptr_ty);
|
||||||
|
}
|
||||||
|
TyKind::Ref(mutbl, _, base_ty) => {
|
||||||
|
let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
|
||||||
|
_ = self.coerce(Some(expr), &ty, &ptr_ty);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let diverge = asm.options.contains(AsmOptions::NORETURN);
|
||||||
|
asm.operands.iter().for_each(|operand| match *operand {
|
||||||
|
AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true),
|
||||||
|
AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => {
|
||||||
|
check_expr_asm_operand(expr, false)
|
||||||
|
}
|
||||||
|
AsmOperand::Out { expr: None, .. } => (),
|
||||||
|
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||||
|
check_expr_asm_operand(in_expr, true);
|
||||||
|
if let Some(out_expr) = out_expr {
|
||||||
|
check_expr_asm_operand(out_expr, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FIXME
|
||||||
|
AsmOperand::Label(_) => (),
|
||||||
|
// FIXME
|
||||||
|
AsmOperand::Const(_) => (),
|
||||||
|
// FIXME
|
||||||
|
AsmOperand::Sym(_) => (),
|
||||||
|
});
|
||||||
|
if diverge {
|
||||||
|
self.result.standard_types.never.clone()
|
||||||
|
} else {
|
||||||
self.result.standard_types.unit.clone()
|
self.result.standard_types.unit.clone()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// use a new type variable if we got unknown here
|
// use a new type variable if we got unknown here
|
||||||
let ty = self.insert_type_vars_shallow(ty);
|
let ty = self.insert_type_vars_shallow(ty);
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
use chalk_ir::{cast::Cast, Mutability};
|
use chalk_ir::{cast::Cast, Mutability};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
|
hir::{
|
||||||
|
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp,
|
||||||
|
},
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
|
@ -39,10 +41,25 @@ impl InferenceContext<'_> {
|
||||||
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
|
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
|
||||||
match &self.body[tgt_expr] {
|
match &self.body[tgt_expr] {
|
||||||
Expr::Missing => (),
|
Expr::Missing => (),
|
||||||
Expr::InlineAsm(e) => e
|
Expr::InlineAsm(e) => {
|
||||||
.template
|
e.operands.iter().for_each(|op| match op {
|
||||||
.iter()
|
AsmOperand::In { expr, .. }
|
||||||
.for_each(|&expr| self.infer_mut_expr_without_adjust(expr, Mutability::Not)),
|
| AsmOperand::Out { expr: Some(expr), .. }
|
||||||
|
| AsmOperand::InOut { expr, .. } => {
|
||||||
|
self.infer_mut_expr_without_adjust(*expr, Mutability::Not)
|
||||||
|
}
|
||||||
|
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||||
|
self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not);
|
||||||
|
if let Some(out_expr) = out_expr {
|
||||||
|
self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AsmOperand::Out { expr: None, .. }
|
||||||
|
| AsmOperand::Label(_)
|
||||||
|
| AsmOperand::Sym(_)
|
||||||
|
| AsmOperand::Const(_) => (),
|
||||||
|
});
|
||||||
|
}
|
||||||
Expr::OffsetOf(_) => (),
|
Expr::OffsetOf(_) => (),
|
||||||
&Expr::If { condition, then_branch, else_branch } => {
|
&Expr::If { condition, then_branch, else_branch } => {
|
||||||
self.infer_mut_expr(condition, Mutability::Not);
|
self.infer_mut_expr(condition, Mutability::Not);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use expect_test::expect;
|
use expect_test::expect;
|
||||||
use test_utils::{bench, bench_fixture, skip_slow_tests};
|
use test_utils::{bench, bench_fixture, skip_slow_tests};
|
||||||
|
|
||||||
use crate::tests::check_infer_with_mismatches;
|
use crate::tests::{check_infer_with_mismatches, check_no_mismatches};
|
||||||
|
|
||||||
use super::{check_infer, check_types};
|
use super::{check_infer, check_types};
|
||||||
|
|
||||||
|
@ -1406,3 +1406,100 @@ fn foo(t: Tensor) {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn asm_unit() {
|
||||||
|
check_no_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: asm
|
||||||
|
fn unit() {
|
||||||
|
asm!("")
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn asm_no_return() {
|
||||||
|
check_no_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: asm
|
||||||
|
fn unit() -> ! {
|
||||||
|
asm!("", options(noreturn))
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn asm_things() {
|
||||||
|
check_infer(
|
||||||
|
r#"
|
||||||
|
//- minicore: asm, concat
|
||||||
|
fn main() {
|
||||||
|
unsafe {
|
||||||
|
let foo = 1;
|
||||||
|
let mut o = 0;
|
||||||
|
asm!(
|
||||||
|
"%input = OpLoad _ {0}",
|
||||||
|
concat!("%result = ", bar, " _ %input"),
|
||||||
|
"OpStore {1} %result",
|
||||||
|
in(reg) &foo,
|
||||||
|
in(reg) &mut o,
|
||||||
|
);
|
||||||
|
o
|
||||||
|
|
||||||
|
let thread_id: usize;
|
||||||
|
asm!("
|
||||||
|
mov {0}, gs:[0x30]
|
||||||
|
mov {0}, [{0}+0x48]
|
||||||
|
", out(reg) thread_id, options(pure, readonly, nostack));
|
||||||
|
|
||||||
|
static UNMAP_BASE: usize;
|
||||||
|
const MEM_RELEASE: usize;
|
||||||
|
static VirtualFree: usize;
|
||||||
|
const OffPtr: usize;
|
||||||
|
const OffFn: usize;
|
||||||
|
asm!("
|
||||||
|
push {free_type}
|
||||||
|
push {free_size}
|
||||||
|
push {base}
|
||||||
|
|
||||||
|
mov eax, fs:[30h]
|
||||||
|
mov eax, [eax+8h]
|
||||||
|
add eax, {off_fn}
|
||||||
|
mov [eax-{off_fn}+{off_ptr}], eax
|
||||||
|
|
||||||
|
push eax
|
||||||
|
|
||||||
|
jmp {virtual_free}
|
||||||
|
",
|
||||||
|
off_ptr = const OffPtr,
|
||||||
|
off_fn = const OffFn,
|
||||||
|
|
||||||
|
free_size = const 0,
|
||||||
|
free_type = const MEM_RELEASE,
|
||||||
|
|
||||||
|
virtual_free = sym VirtualFree,
|
||||||
|
|
||||||
|
base = sym UNMAP_BASE,
|
||||||
|
options(noreturn),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
!0..122 'builti...muto,)': ()
|
||||||
|
!0..190 'builti...tack))': ()
|
||||||
|
!0..449 'builti...urn),)': !
|
||||||
|
10..1254 '{ ... } }': ()
|
||||||
|
16..1252 'unsafe... }': ()
|
||||||
|
37..40 'foo': i32
|
||||||
|
43..44 '1': i32
|
||||||
|
58..63 'mut o': i32
|
||||||
|
66..67 '0': i32
|
||||||
|
281..282 'o': i32
|
||||||
|
296..305 'thread_id': usize
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -368,7 +368,6 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
| BuiltinFnLikeExpander::File
|
| BuiltinFnLikeExpander::File
|
||||||
| BuiltinFnLikeExpander::ModulePath
|
| BuiltinFnLikeExpander::ModulePath
|
||||||
| BuiltinFnLikeExpander::Asm
|
| BuiltinFnLikeExpander::Asm
|
||||||
| BuiltinFnLikeExpander::LlvmAsm
|
|
||||||
| BuiltinFnLikeExpander::GlobalAsm
|
| BuiltinFnLikeExpander::GlobalAsm
|
||||||
| BuiltinFnLikeExpander::LogSyntax
|
| BuiltinFnLikeExpander::LogSyntax
|
||||||
| BuiltinFnLikeExpander::TraceMacros
|
| BuiltinFnLikeExpander::TraceMacros
|
||||||
|
|
|
@ -166,10 +166,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||||
<span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
|
<span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
|
||||||
<span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
|
<span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
|
||||||
<span class="macro default_library library">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
|
<span class="macro default_library library">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
|
||||||
<span class="string_literal macro">"mov </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">, </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>
|
<span class="string_literal macro">"mov {0}, {1}"</span><span class="comma macro">,</span>
|
||||||
<span class="string_literal macro">"add </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">, 5"</span><span class="comma macro">,</span>
|
<span class="string_literal macro">"add {0}, 5"</span><span class="comma macro">,</span>
|
||||||
<span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span>
|
<span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="unresolved_reference macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">o</span><span class="comma macro">,</span>
|
||||||
<span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span>
|
<span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="unresolved_reference macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">i</span><span class="comma macro">,</span>
|
||||||
<span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
|
|
||||||
<span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span>
|
<span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span>
|
||||||
|
|
|
@ -329,9 +329,11 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let op = p.start();
|
||||||
// Parse clobber_abi
|
// Parse clobber_abi
|
||||||
if p.eat_contextual_kw(T![clobber_abi]) {
|
if p.eat_contextual_kw(T![clobber_abi]) {
|
||||||
parse_clobber_abi(p);
|
parse_clobber_abi(p);
|
||||||
|
op.complete(p, ASM_CLOBBER_ABI);
|
||||||
allow_templates = false;
|
allow_templates = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -339,6 +341,7 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||||
// Parse options
|
// Parse options
|
||||||
if p.eat_contextual_kw(T![options]) {
|
if p.eat_contextual_kw(T![options]) {
|
||||||
parse_options(p);
|
parse_options(p);
|
||||||
|
op.complete(p, ASM_OPTIONS);
|
||||||
allow_templates = false;
|
allow_templates = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -353,27 +356,14 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let op = p.start();
|
let dir_spec = p.start();
|
||||||
if p.eat(T![in]) {
|
if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) {
|
||||||
|
dir_spec.complete(p, ASM_DIR_SPEC);
|
||||||
parse_reg(p);
|
parse_reg(p);
|
||||||
expr(p);
|
expr(p);
|
||||||
op.complete(p, ASM_REG_OPERAND);
|
op.complete(p, ASM_REG_OPERAND);
|
||||||
} else if p.eat_contextual_kw(T![out]) {
|
} else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) {
|
||||||
parse_reg(p);
|
dir_spec.complete(p, ASM_DIR_SPEC);
|
||||||
expr(p);
|
|
||||||
op.complete(p, ASM_REG_OPERAND);
|
|
||||||
} else if p.eat_contextual_kw(T![lateout]) {
|
|
||||||
parse_reg(p);
|
|
||||||
expr(p);
|
|
||||||
op.complete(p, ASM_REG_OPERAND);
|
|
||||||
} else if p.eat_contextual_kw(T![inout]) {
|
|
||||||
parse_reg(p);
|
|
||||||
expr(p);
|
|
||||||
if p.eat(T![=>]) {
|
|
||||||
expr(p);
|
|
||||||
}
|
|
||||||
op.complete(p, ASM_REG_OPERAND);
|
|
||||||
} else if p.eat_contextual_kw(T![inlateout]) {
|
|
||||||
parse_reg(p);
|
parse_reg(p);
|
||||||
expr(p);
|
expr(p);
|
||||||
if p.eat(T![=>]) {
|
if p.eat(T![=>]) {
|
||||||
|
@ -381,21 +371,26 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||||
}
|
}
|
||||||
op.complete(p, ASM_REG_OPERAND);
|
op.complete(p, ASM_REG_OPERAND);
|
||||||
} else if p.eat_contextual_kw(T![label]) {
|
} else if p.eat_contextual_kw(T![label]) {
|
||||||
|
dir_spec.abandon(p);
|
||||||
block_expr(p);
|
block_expr(p);
|
||||||
op.complete(p, ASM_LABEL);
|
op.complete(p, ASM_LABEL);
|
||||||
} else if p.eat(T![const]) {
|
} else if p.eat(T![const]) {
|
||||||
|
dir_spec.abandon(p);
|
||||||
expr(p);
|
expr(p);
|
||||||
op.complete(p, ASM_CONST);
|
op.complete(p, ASM_CONST);
|
||||||
} else if p.eat_contextual_kw(T![sym]) {
|
} else if p.eat_contextual_kw(T![sym]) {
|
||||||
expr(p);
|
dir_spec.abandon(p);
|
||||||
|
paths::type_path(p);
|
||||||
op.complete(p, ASM_SYM);
|
op.complete(p, ASM_SYM);
|
||||||
} else if allow_templates {
|
} else if allow_templates {
|
||||||
|
dir_spec.abandon(p);
|
||||||
op.abandon(p);
|
op.abandon(p);
|
||||||
if expr(p).is_none() {
|
if expr(p).is_none() {
|
||||||
p.err_and_bump("expected asm template");
|
p.err_and_bump("expected asm template");
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
dir_spec.abandon(p);
|
||||||
op.abandon(p);
|
op.abandon(p);
|
||||||
p.err_and_bump("expected asm operand");
|
p.err_and_bump("expected asm operand");
|
||||||
if p.at(T!['}']) {
|
if p.at(T!['}']) {
|
||||||
|
@ -424,11 +419,12 @@ fn parse_options(p: &mut Parser<'_>) {
|
||||||
T![att_syntax],
|
T![att_syntax],
|
||||||
T![raw],
|
T![raw],
|
||||||
];
|
];
|
||||||
|
let m = p.start();
|
||||||
if !OPTIONS.iter().any(|&syntax| p.eat(syntax)) {
|
if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) {
|
||||||
p.err_and_bump("expected asm option");
|
p.err_and_bump("expected asm option");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
m.complete(p, ASM_OPTION);
|
||||||
|
|
||||||
// Allow trailing commas
|
// Allow trailing commas
|
||||||
if p.eat(T![')']) {
|
if p.eat(T![')']) {
|
||||||
|
|
|
@ -411,13 +411,11 @@ AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')'
|
||||||
AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind'
|
AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind'
|
||||||
// options := "options(" option *("," option) [","] ")"
|
// options := "options(" option *("," option) [","] ")"
|
||||||
AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')'
|
AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')'
|
||||||
// operand := reg_operand / clobber_abi / options
|
|
||||||
AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel
|
|
||||||
AsmLabel = 'label' BlockExpr
|
AsmLabel = 'label' BlockExpr
|
||||||
AsmSym = 'sym' Expr
|
AsmSym = 'sym' Path
|
||||||
AsmConst = 'const' Expr
|
AsmConst = 'const' Expr
|
||||||
|
// operand := reg_operand / clobber_abi / options
|
||||||
|
AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel | AsmSym | AsmConst
|
||||||
|
|
||||||
FormatArgsExpr =
|
FormatArgsExpr =
|
||||||
Attr* 'builtin' '#' 'format_args' '('
|
Attr* 'builtin' '#' 'format_args' '('
|
||||||
|
|
|
@ -250,7 +250,7 @@ pub struct AsmSym {
|
||||||
}
|
}
|
||||||
impl AsmSym {
|
impl AsmSym {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn sym_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![sym]) }
|
pub fn sym_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![sym]) }
|
||||||
}
|
}
|
||||||
|
@ -2225,9 +2225,11 @@ impl ast::HasVisibility for Adt {}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum AsmOperand {
|
pub enum AsmOperand {
|
||||||
AsmClobberAbi(AsmClobberAbi),
|
AsmClobberAbi(AsmClobberAbi),
|
||||||
|
AsmConst(AsmConst),
|
||||||
AsmLabel(AsmLabel),
|
AsmLabel(AsmLabel),
|
||||||
AsmOptions(AsmOptions),
|
AsmOptions(AsmOptions),
|
||||||
AsmRegOperand(AsmRegOperand),
|
AsmRegOperand(AsmRegOperand),
|
||||||
|
AsmSym(AsmSym),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -4591,6 +4593,10 @@ impl From<AsmClobberAbi> for AsmOperand {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(node: AsmClobberAbi) -> AsmOperand { AsmOperand::AsmClobberAbi(node) }
|
fn from(node: AsmClobberAbi) -> AsmOperand { AsmOperand::AsmClobberAbi(node) }
|
||||||
}
|
}
|
||||||
|
impl From<AsmConst> for AsmOperand {
|
||||||
|
#[inline]
|
||||||
|
fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) }
|
||||||
|
}
|
||||||
impl From<AsmLabel> for AsmOperand {
|
impl From<AsmLabel> for AsmOperand {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) }
|
fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) }
|
||||||
|
@ -4603,18 +4609,27 @@ impl From<AsmRegOperand> for AsmOperand {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) }
|
fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) }
|
||||||
}
|
}
|
||||||
|
impl From<AsmSym> for AsmOperand {
|
||||||
|
#[inline]
|
||||||
|
fn from(node: AsmSym) -> AsmOperand { AsmOperand::AsmSym(node) }
|
||||||
|
}
|
||||||
impl AstNode for AsmOperand {
|
impl AstNode for AsmOperand {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn can_cast(kind: SyntaxKind) -> bool {
|
fn can_cast(kind: SyntaxKind) -> bool {
|
||||||
matches!(kind, ASM_CLOBBER_ABI | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND)
|
matches!(
|
||||||
|
kind,
|
||||||
|
ASM_CLOBBER_ABI | ASM_CONST | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND | ASM_SYM
|
||||||
|
)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||||
let res = match syntax.kind() {
|
let res = match syntax.kind() {
|
||||||
ASM_CLOBBER_ABI => AsmOperand::AsmClobberAbi(AsmClobberAbi { syntax }),
|
ASM_CLOBBER_ABI => AsmOperand::AsmClobberAbi(AsmClobberAbi { syntax }),
|
||||||
|
ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }),
|
||||||
ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }),
|
ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }),
|
||||||
ASM_OPTIONS => AsmOperand::AsmOptions(AsmOptions { syntax }),
|
ASM_OPTIONS => AsmOperand::AsmOptions(AsmOptions { syntax }),
|
||||||
ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }),
|
ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }),
|
||||||
|
ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(res)
|
Some(res)
|
||||||
|
@ -4623,9 +4638,11 @@ impl AstNode for AsmOperand {
|
||||||
fn syntax(&self) -> &SyntaxNode {
|
fn syntax(&self) -> &SyntaxNode {
|
||||||
match self {
|
match self {
|
||||||
AsmOperand::AsmClobberAbi(it) => &it.syntax,
|
AsmOperand::AsmClobberAbi(it) => &it.syntax,
|
||||||
|
AsmOperand::AsmConst(it) => &it.syntax,
|
||||||
AsmOperand::AsmLabel(it) => &it.syntax,
|
AsmOperand::AsmLabel(it) => &it.syntax,
|
||||||
AsmOperand::AsmOptions(it) => &it.syntax,
|
AsmOperand::AsmOptions(it) => &it.syntax,
|
||||||
AsmOperand::AsmRegOperand(it) => &it.syntax,
|
AsmOperand::AsmRegOperand(it) => &it.syntax,
|
||||||
|
AsmOperand::AsmSym(it) => &it.syntax,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue