-
Notifications
You must be signed in to change notification settings - Fork 12.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
612 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
Oops, something went wrong.