hir_def/expr_store/lower/
asm.rs1use hir_expand::name::Name;
3use intern::Symbol;
4use rustc_hash::{FxHashMap, FxHashSet};
5use syntax::{
6 AstNode, AstPtr, AstToken, T,
7 ast::{self, HasName, IsString},
8};
9use tt::TextRange;
10
11use crate::{
12 expr_store::lower::{ExprCollector, FxIndexSet},
13 hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmKind, InlineAsmRegOrRegClass},
14};
15
16impl ExprCollector<'_> {
17 pub(super) fn lower_inline_asm(
18 &mut self,
19 asm: ast::AsmExpr,
20 syntax_ptr: AstPtr<ast::Expr>,
21 ) -> ExprId {
22 let mut clobber_abis = FxIndexSet::default();
23 let mut operands = vec![];
24 let mut options = AsmOptions::empty();
25
26 let mut named_pos: FxHashMap<usize, Symbol> = Default::default();
27 let mut named_args: FxHashMap<Symbol, usize> = Default::default();
28 let mut reg_args: FxHashSet<usize> = Default::default();
29 for piece in asm.asm_pieces() {
30 if !self.check_cfg(&piece) {
31 continue;
32 }
33
34 let slot = operands.len();
35 let mut lower_reg = |reg: Option<ast::AsmRegSpec>| {
36 let reg = reg?;
37 if let Some(string) = reg.string_token() {
38 reg_args.insert(slot);
39 Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text())))
40 } else {
41 reg.name_ref().map(|name_ref| {
42 InlineAsmRegOrRegClass::RegClass(Symbol::intern(name_ref.text()))
43 })
44 }
45 };
46
47 let op = match piece {
48 ast::AsmPiece::AsmClobberAbi(clobber_abi) => {
49 if let Some(abi_name) = clobber_abi.string_token() {
50 clobber_abis.insert(Symbol::intern(abi_name.text()));
51 }
52 continue;
53 }
54 ast::AsmPiece::AsmOptions(opt) => {
55 opt.asm_options().for_each(|opt| {
56 options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) {
57 T![att_syntax] => AsmOptions::ATT_SYNTAX,
58 T![may_unwind] => AsmOptions::MAY_UNWIND,
59 T![nomem] => AsmOptions::NOMEM,
60 T![noreturn] => AsmOptions::NORETURN,
61 T![nostack] => AsmOptions::NOSTACK,
62 T![preserves_flags] => AsmOptions::PRESERVES_FLAGS,
63 T![pure] => AsmOptions::PURE,
64 T![raw] => AsmOptions::RAW,
65 T![readonly] => AsmOptions::READONLY,
66 _ => return,
67 }
68 });
69 continue;
70 }
71 ast::AsmPiece::AsmOperandNamed(op) => {
72 let name = op.name().map(|name| Symbol::intern(name.text()));
73 if let Some(name) = &name {
74 named_args.insert(name.clone(), slot);
75 named_pos.insert(slot, name.clone());
76 }
77 let Some(op) = op.asm_operand() else { continue };
78 (
79 name.map(Name::new_symbol_root),
80 match op {
81 ast::AsmOperand::AsmRegOperand(op) => {
82 let Some(dir_spec) = op.asm_dir_spec() else {
83 continue;
84 };
85 let Some(reg) = lower_reg(op.asm_reg_spec()) else {
86 continue;
87 };
88 if dir_spec.in_token().is_some() {
89 let expr = self.collect_expr_opt(
90 op.asm_operand_expr().and_then(|it| it.in_expr()),
91 );
92 AsmOperand::In { reg, expr }
93 } else if dir_spec.out_token().is_some() {
94 let expr = op
95 .asm_operand_expr()
96 .and_then(|it| it.in_expr())
97 .filter(|it| !matches!(it, ast::Expr::UnderscoreExpr(_)))
98 .map(|expr| self.collect_expr(expr));
99 AsmOperand::Out { reg, expr, late: false }
100 } else if dir_spec.lateout_token().is_some() {
101 let expr = op
102 .asm_operand_expr()
103 .and_then(|it| it.in_expr())
104 .filter(|it| !matches!(it, ast::Expr::UnderscoreExpr(_)))
105 .map(|expr| self.collect_expr(expr));
106
107 AsmOperand::Out { reg, expr, late: true }
108 } else if dir_spec.inout_token().is_some() {
109 let Some(op_expr) = op.asm_operand_expr() else { continue };
110 let in_expr = self.collect_expr_opt(op_expr.in_expr());
111 match op_expr.fat_arrow_token().is_some() {
112 true => {
113 let out_expr = op_expr
114 .out_expr()
115 .filter(|it| {
116 !matches!(it, ast::Expr::UnderscoreExpr(_))
117 })
118 .map(|expr| self.collect_expr(expr));
119
120 AsmOperand::SplitInOut {
121 reg,
122 in_expr,
123 out_expr,
124 late: false,
125 }
126 }
127 false => {
128 AsmOperand::InOut { reg, expr: in_expr, late: false }
129 }
130 }
131 } else if dir_spec.inlateout_token().is_some() {
132 let Some(op_expr) = op.asm_operand_expr() else { continue };
133 let in_expr = self.collect_expr_opt(op_expr.in_expr());
134 match op_expr.fat_arrow_token().is_some() {
135 true => {
136 let out_expr = op_expr
137 .out_expr()
138 .filter(|it| {
139 !matches!(it, ast::Expr::UnderscoreExpr(_))
140 })
141 .map(|expr| self.collect_expr(expr));
142
143 AsmOperand::SplitInOut {
144 reg,
145 in_expr,
146 out_expr,
147 late: true,
148 }
149 }
150 false => {
151 AsmOperand::InOut { reg, expr: in_expr, late: true }
152 }
153 }
154 } else {
155 continue;
156 }
157 }
158 ast::AsmOperand::AsmLabel(l) => {
159 AsmOperand::Label(self.collect_block_opt(l.block_expr()))
160 }
161 ast::AsmOperand::AsmConst(c) => {
162 AsmOperand::Const(self.collect_expr_opt(c.expr()))
163 }
164 ast::AsmOperand::AsmSym(s) => {
165 let Some(path) = s.path().and_then(|p| {
166 self.lower_path(
167 p,
168 &mut ExprCollector::impl_trait_error_allocator,
169 )
170 }) else {
171 continue;
172 };
173 AsmOperand::Sym(path)
174 }
175 },
176 )
177 }
178 };
179 operands.push(op);
180 }
181
182 let mut mappings = vec![];
183 let mut curarg = 0;
184 if !options.contains(AsmOptions::RAW) {
185 asm.template()
187 .enumerate()
188 .filter_map(|(idx, it)| Some((idx, it.clone(), self.expand_macros_to_string(it)?)))
189 .for_each(|(idx, expr, (s, is_direct_literal))| {
190 mappings.resize_with(idx + 1, Vec::default);
191 let Ok(text) = s.value() else {
192 return;
193 };
194 let mappings = &mut mappings[idx];
195 let template_snippet = match expr {
196 ast::Expr::Literal(literal) => match literal.kind() {
197 ast::LiteralKind::String(s) => Some(s.text().to_owned()),
198 _ => None,
199 },
200 _ => None,
201 };
202 let str_style = match s.quote_offsets() {
203 Some(offsets) => {
204 let raw = usize::from(offsets.quotes.0.len()) - 1;
205 (raw != 0).then(|| raw - 1)
207 }
208 None => None,
209 };
210
211 let mut parser = rustc_parse_format::Parser::new(
212 &text,
213 str_style,
214 template_snippet,
215 false,
216 rustc_parse_format::ParseMode::InlineAsm,
217 );
218 parser.curarg = curarg;
219
220 let mut unverified_pieces = Vec::new();
221 while let Some(piece) = parser.next() {
222 if !parser.errors.is_empty() {
223 break;
224 } else {
225 unverified_pieces.push(piece);
226 }
227 }
228
229 curarg = parser.curarg;
230
231 let to_span = |inner_span: std::ops::Range<usize>| {
232 is_direct_literal.then(|| {
233 TextRange::new(
234 inner_span.start.try_into().unwrap(),
235 inner_span.end.try_into().unwrap(),
236 )
237 })
238 };
239 for piece in unverified_pieces {
240 match piece {
241 rustc_parse_format::Piece::Lit(_) => {}
242 rustc_parse_format::Piece::NextArgument(arg) => {
243 let (operand_idx, _name) = match arg.position {
246 rustc_parse_format::ArgumentIs(idx)
247 | rustc_parse_format::ArgumentImplicitlyIs(idx) => {
248 if idx >= operands.len()
249 || named_pos.contains_key(&idx)
250 || reg_args.contains(&idx)
251 {
252 (None, None)
253 } else {
254 (Some(idx), None)
255 }
256 }
257 rustc_parse_format::ArgumentNamed(name) => {
258 let name = Symbol::intern(name);
259 (
260 named_args.get(&name).copied(),
261 Some(Name::new_symbol_root(name)),
262 )
263 }
264 };
265
266 if let Some(operand_idx) = operand_idx
267 && let Some(position_span) = to_span(arg.position_span)
268 {
269 mappings.push((position_span, operand_idx));
270 }
271 }
272 }
273 }
274 })
275 };
276
277 let kind = if asm.global_asm_token().is_some() {
278 InlineAsmKind::GlobalAsm
279 } else if asm.naked_asm_token().is_some() {
280 InlineAsmKind::NakedAsm
281 } else {
282 InlineAsmKind::Asm
283 };
284
285 let idx = self.alloc_expr(
286 Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options, kind }),
287 syntax_ptr,
288 );
289 self.store
290 .template_map
291 .get_or_insert_with(Default::default)
292 .asm_to_captures
293 .insert(idx, mappings);
294 idx
295 }
296}