Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 1328b52

Browse files
committed
Lower asm expressions
1 parent 7222f2d commit 1328b52

File tree

14 files changed

+612
-97
lines changed

14 files changed

+612
-97
lines changed

src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
22
//! representation.
33
4+
mod asm;
5+
46
use std::mem;
57

68
use base_db::CrateId;
@@ -35,8 +37,8 @@ use crate::{
3537
FormatPlaceholder, FormatSign, FormatTrait,
3638
},
3739
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
38-
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
39-
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
40+
Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
41+
PatId, RecordFieldPat, RecordLitField, Statement,
4042
},
4143
item_scope::BuiltinShadowMode,
4244
lang_item::LangItem,
@@ -693,13 +695,7 @@ impl ExprCollector<'_> {
693695
}
694696
}
695697
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
696-
ast::Expr::AsmExpr(e) => {
697-
let template = e.template().map(|it| self.collect_expr(it)).collect();
698-
self.alloc_expr(
699-
Expr::InlineAsm(InlineAsm { template, operands: Box::default() }),
700-
syntax_ptr,
701-
)
702-
}
698+
ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr),
703699
ast::Expr::OffsetOfExpr(e) => {
704700
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
705701
let fields = e.fields().map(|it| it.as_name()).collect();
@@ -2064,6 +2060,7 @@ impl ExprCollector<'_> {
20642060
is_assignee_expr: false,
20652061
})
20662062
}
2063+
20672064
// endregion: format
20682065

20692066
fn lang_path(&self, lang: LangItem) -> Option<Path> {
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
use hir_expand::name::Name;
2+
use intern::Symbol;
3+
use rustc_hash::{FxHashMap, FxHashSet};
4+
use syntax::{
5+
ast::{self, HasName, IsString},
6+
AstNode, AstPtr, AstToken, T,
7+
};
8+
use tt::{TextRange, TextSize};
9+
10+
use crate::{
11+
body::lower::{ExprCollector, FxIndexSet},
12+
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
13+
};
14+
15+
impl ExprCollector<'_> {
16+
pub(super) fn lower_inline_asm(
17+
&mut self,
18+
asm: ast::AsmExpr,
19+
syntax_ptr: AstPtr<ast::Expr>,
20+
) -> ExprId {
21+
let mut clobber_abis = FxIndexSet::default();
22+
let mut operands = vec![];
23+
let mut options = AsmOptions::empty();
24+
25+
let mut named_pos: FxHashMap<usize, Symbol> = Default::default();
26+
let mut named_args: FxHashMap<Symbol, usize> = Default::default();
27+
let mut reg_args: FxHashSet<usize> = Default::default();
28+
for operand in asm.asm_operands() {
29+
let slot = operands.len();
30+
let mut lower_reg = |reg: Option<ast::AsmRegSpec>| {
31+
let reg = reg?;
32+
if let Some(string) = reg.string_token() {
33+
reg_args.insert(slot);
34+
Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text())))
35+
} else {
36+
reg.name_ref().map(|name_ref| {
37+
InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text()))
38+
})
39+
}
40+
};
41+
42+
let op = match operand {
43+
ast::AsmOperand::AsmClobberAbi(clobber_abi) => {
44+
if let Some(abi_name) = clobber_abi.string_token() {
45+
clobber_abis.insert(Symbol::intern(abi_name.text()));
46+
}
47+
continue;
48+
}
49+
ast::AsmOperand::AsmOptions(opt) => {
50+
opt.asm_options().for_each(|opt| {
51+
options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) {
52+
T![att_syntax] => AsmOptions::ATT_SYNTAX,
53+
T![may_unwind] => AsmOptions::MAY_UNWIND,
54+
T![nomem] => AsmOptions::NOMEM,
55+
T![noreturn] => AsmOptions::NORETURN,
56+
T![nostack] => AsmOptions::NOSTACK,
57+
T![preserves_flags] => AsmOptions::PRESERVES_FLAGS,
58+
T![pure] => AsmOptions::PURE,
59+
T![raw] => AsmOptions::RAW,
60+
T![readonly] => AsmOptions::READONLY,
61+
_ => return,
62+
}
63+
});
64+
continue;
65+
}
66+
ast::AsmOperand::AsmRegOperand(op) => {
67+
let Some(dir_spec) = op.asm_dir_spec() else {
68+
continue;
69+
};
70+
let Some(reg) = lower_reg(op.asm_reg_spec()) else {
71+
continue;
72+
};
73+
if let Some(name) = op.name() {
74+
let sym = Symbol::intern(&name.text());
75+
named_args.insert(sym.clone(), slot);
76+
named_pos.insert(slot, sym);
77+
}
78+
if dir_spec.in_token().is_some() {
79+
let expr = self
80+
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
81+
AsmOperand::In { reg, expr }
82+
} else if dir_spec.out_token().is_some() {
83+
let expr = self
84+
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
85+
AsmOperand::Out { reg, expr: Some(expr), late: false }
86+
} else if dir_spec.lateout_token().is_some() {
87+
let expr = self
88+
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
89+
AsmOperand::Out { reg, expr: Some(expr), late: true }
90+
} else if dir_spec.inout_token().is_some() {
91+
let Some(op_expr) = op.asm_operand_expr() else { continue };
92+
let in_expr = self.collect_expr_opt(op_expr.in_expr());
93+
let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
94+
match out_expr {
95+
Some(out_expr) => AsmOperand::SplitInOut {
96+
reg,
97+
in_expr,
98+
out_expr: Some(out_expr),
99+
late: false,
100+
},
101+
None => AsmOperand::InOut { reg, expr: in_expr, late: false },
102+
}
103+
} else if dir_spec.inlateout_token().is_some() {
104+
let Some(op_expr) = op.asm_operand_expr() else { continue };
105+
let in_expr = self.collect_expr_opt(op_expr.in_expr());
106+
let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
107+
match out_expr {
108+
Some(out_expr) => AsmOperand::SplitInOut {
109+
reg,
110+
in_expr,
111+
out_expr: Some(out_expr),
112+
late: false,
113+
},
114+
None => AsmOperand::InOut { reg, expr: in_expr, late: false },
115+
}
116+
} else {
117+
continue;
118+
}
119+
}
120+
ast::AsmOperand::AsmLabel(l) => {
121+
AsmOperand::Label(self.collect_block_opt(l.block_expr()))
122+
}
123+
ast::AsmOperand::AsmConst(c) => AsmOperand::Const(self.collect_expr_opt(c.expr())),
124+
ast::AsmOperand::AsmSym(s) => {
125+
let Some(path) = s.path().and_then(|p| self.expander.parse_path(self.db, p))
126+
else {
127+
continue;
128+
};
129+
AsmOperand::Sym(path)
130+
}
131+
};
132+
operands.push(op);
133+
}
134+
135+
let mut mappings = vec![];
136+
let mut curarg = 0;
137+
if !options.contains(AsmOptions::RAW) {
138+
// Don't treat raw asm as a format string.
139+
asm.template()
140+
.filter_map(|it| Some((it.clone(), self.expand_macros_to_string(it)?)))
141+
.for_each(|(expr, (s, is_direct_literal))| {
142+
let Ok(text) = s.value() else {
143+
return;
144+
};
145+
let template_snippet = match expr {
146+
ast::Expr::Literal(literal) => match literal.kind() {
147+
ast::LiteralKind::String(s) => Some(s.text().to_owned()),
148+
_ => None,
149+
},
150+
_ => None,
151+
};
152+
let str_style = match s.quote_offsets() {
153+
Some(offsets) => {
154+
let raw = usize::from(offsets.quotes.0.len()) - 1;
155+
// subtract 1 for the `r` prefix
156+
(raw != 0).then(|| raw - 1)
157+
}
158+
None => None,
159+
};
160+
161+
let mut parser = rustc_parse_format::Parser::new(
162+
&text,
163+
str_style,
164+
template_snippet,
165+
false,
166+
rustc_parse_format::ParseMode::InlineAsm,
167+
);
168+
parser.curarg = curarg;
169+
170+
let mut unverified_pieces = Vec::new();
171+
while let Some(piece) = parser.next() {
172+
if !parser.errors.is_empty() {
173+
break;
174+
} else {
175+
unverified_pieces.push(piece);
176+
}
177+
}
178+
179+
curarg = parser.curarg;
180+
181+
let to_span = |inner_span: rustc_parse_format::InnerSpan| {
182+
is_direct_literal.then(|| {
183+
TextRange::new(
184+
inner_span.start.try_into().unwrap(),
185+
inner_span.end.try_into().unwrap(),
186+
) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
187+
})
188+
};
189+
for piece in unverified_pieces {
190+
match piece {
191+
rustc_parse_format::Piece::String(_) => {}
192+
rustc_parse_format::Piece::NextArgument(arg) => {
193+
// let span = arg_spans.next();
194+
195+
let _operand_idx = match arg.position {
196+
rustc_parse_format::ArgumentIs(idx)
197+
| rustc_parse_format::ArgumentImplicitlyIs(idx) => {
198+
if idx >= operands.len()
199+
|| named_pos.contains_key(&idx)
200+
|| reg_args.contains(&idx)
201+
{
202+
None
203+
} else {
204+
Some(idx)
205+
}
206+
}
207+
rustc_parse_format::ArgumentNamed(name) => {
208+
let name = Symbol::intern(name);
209+
if let Some(position_span) = to_span(arg.position_span) {
210+
mappings.push((
211+
position_span,
212+
Name::new_symbol_root(name.clone()),
213+
));
214+
}
215+
named_args.get(&name).copied()
216+
}
217+
};
218+
}
219+
}
220+
}
221+
})
222+
};
223+
let idx = self.alloc_expr(
224+
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
225+
syntax_ptr,
226+
);
227+
self.source_map.format_args_template_map.insert(idx, mappings);
228+
idx
229+
}
230+
}

0 commit comments

Comments
 (0)