mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +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`
|
||||
//! representation.
|
||||
|
||||
mod asm;
|
||||
|
||||
use std::mem;
|
||||
|
||||
use base_db::CrateId;
|
||||
|
@ -35,8 +37,8 @@ use crate::{
|
|||
FormatPlaceholder, FormatSign, FormatTrait,
|
||||
},
|
||||
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
||||
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
|
||||
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
||||
Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
|
||||
PatId, RecordFieldPat, RecordLitField, Statement,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
lang_item::LangItem,
|
||||
|
@ -693,13 +695,7 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
|
||||
ast::Expr::AsmExpr(e) => {
|
||||
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::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr),
|
||||
ast::Expr::OffsetOfExpr(e) => {
|
||||
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
|
||||
let fields = e.fields().map(|it| it.as_name()).collect();
|
||||
|
@ -2064,6 +2060,7 @@ impl ExprCollector<'_> {
|
|||
is_assignee_expr: false,
|
||||
})
|
||||
}
|
||||
|
||||
// endregion: format
|
||||
|
||||
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)]
|
||||
pub struct InlineAsm {
|
||||
pub template: Box<[ExprId]>,
|
||||
pub operands: Box<[()]>,
|
||||
pub operands: Box<[AsmOperand]>,
|
||||
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)]
|
||||
|
@ -373,7 +485,21 @@ impl Expr {
|
|||
match self {
|
||||
Expr::Missing => {}
|
||||
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 } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
|
|
|
@ -50,11 +50,7 @@ fn main() {
|
|||
let i: u64 = 3;
|
||||
let o: u64;
|
||||
unsafe {
|
||||
builtin #asm ( {
|
||||
$crate::format_args!("mov {0}, {1}");
|
||||
$crate::format_args!("add {0}, 5");
|
||||
}
|
||||
);
|
||||
builtin #asm ("mov {0}, {1}", "add {0}, 5", out (reg)o, in (reg)i, );
|
||||
}
|
||||
}
|
||||
"##]],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue