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