]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast_lowering/src/asm.rs
Rollup merge of #99486 - TaKO8Ki:remove-type-string-comparison-in-check-str-addition...
[rust.git] / compiler / rustc_ast_lowering / src / asm.rs
1 use crate::{ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringExt};
2
3 use super::LoweringContext;
4
5 use rustc_ast::ptr::P;
6 use rustc_ast::*;
7 use rustc_data_structures::fx::FxHashMap;
8 use rustc_data_structures::stable_set::FxHashSet;
9 use rustc_errors::struct_span_err;
10 use rustc_hir as hir;
11 use rustc_hir::def::{DefKind, Res};
12 use rustc_hir::definitions::DefPathData;
13 use rustc_session::parse::feature_err;
14 use rustc_span::{sym, Span};
15 use rustc_target::asm;
16 use std::collections::hash_map::Entry;
17 use std::fmt::Write;
18
19 impl<'a, 'hir> LoweringContext<'a, 'hir> {
20     pub(crate) fn lower_inline_asm(
21         &mut self,
22         sp: Span,
23         asm: &InlineAsm,
24     ) -> &'hir hir::InlineAsm<'hir> {
25         // Rustdoc needs to support asm! from foreign architectures: don't try
26         // lowering the register constraints in this case.
27         let asm_arch =
28             if self.tcx.sess.opts.actually_rustdoc { None } else { self.tcx.sess.asm_arch };
29         if asm_arch.is_none() && !self.tcx.sess.opts.actually_rustdoc {
30             struct_span_err!(
31                 self.tcx.sess,
32                 sp,
33                 E0472,
34                 "inline assembly is unsupported on this target"
35             )
36             .emit();
37         }
38         if let Some(asm_arch) = asm_arch {
39             // Inline assembly is currently only stable for these architectures.
40             let is_stable = matches!(
41                 asm_arch,
42                 asm::InlineAsmArch::X86
43                     | asm::InlineAsmArch::X86_64
44                     | asm::InlineAsmArch::Arm
45                     | asm::InlineAsmArch::AArch64
46                     | asm::InlineAsmArch::RiscV32
47                     | asm::InlineAsmArch::RiscV64
48             );
49             if !is_stable && !self.tcx.features().asm_experimental_arch {
50                 feature_err(
51                     &self.tcx.sess.parse_sess,
52                     sym::asm_experimental_arch,
53                     sp,
54                     "inline assembly is not stable yet on this architecture",
55                 )
56                 .emit();
57             }
58         }
59         if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
60             && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
61             && !self.tcx.sess.opts.actually_rustdoc
62         {
63             self.tcx
64                 .sess
65                 .struct_span_err(sp, "the `att_syntax` option is only supported on x86")
66                 .emit();
67         }
68         if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind {
69             feature_err(
70                 &self.tcx.sess.parse_sess,
71                 sym::asm_unwind,
72                 sp,
73                 "the `may_unwind` option is unstable",
74             )
75             .emit();
76         }
77
78         let mut clobber_abis = FxHashMap::default();
79         if let Some(asm_arch) = asm_arch {
80             for (abi_name, abi_span) in &asm.clobber_abis {
81                 match asm::InlineAsmClobberAbi::parse(asm_arch, &self.tcx.sess.target, *abi_name) {
82                     Ok(abi) => {
83                         // If the abi was already in the list, emit an error
84                         match clobber_abis.get(&abi) {
85                             Some((prev_name, prev_sp)) => {
86                                 let mut err = self.tcx.sess.struct_span_err(
87                                     *abi_span,
88                                     &format!("`{}` ABI specified multiple times", prev_name),
89                                 );
90                                 err.span_label(*prev_sp, "previously specified here");
91
92                                 // Multiple different abi names may actually be the same ABI
93                                 // If the specified ABIs are not the same name, alert the user that they resolve to the same ABI
94                                 let source_map = self.tcx.sess.source_map();
95                                 if source_map.span_to_snippet(*prev_sp)
96                                     != source_map.span_to_snippet(*abi_span)
97                                 {
98                                     err.note("these ABIs are equivalent on the current target");
99                                 }
100
101                                 err.emit();
102                             }
103                             None => {
104                                 clobber_abis.insert(abi, (abi_name, *abi_span));
105                             }
106                         }
107                     }
108                     Err(&[]) => {
109                         self.tcx
110                             .sess
111                             .struct_span_err(
112                                 *abi_span,
113                                 "`clobber_abi` is not supported on this target",
114                             )
115                             .emit();
116                     }
117                     Err(supported_abis) => {
118                         let mut err = self
119                             .tcx
120                             .sess
121                             .struct_span_err(*abi_span, "invalid ABI for `clobber_abi`");
122                         let mut abis = format!("`{}`", supported_abis[0]);
123                         for m in &supported_abis[1..] {
124                             let _ = write!(abis, ", `{}`", m);
125                         }
126                         err.note(&format!(
127                             "the following ABIs are supported on this target: {}",
128                             abis
129                         ));
130                         err.emit();
131                     }
132                 }
133             }
134         }
135
136         // Lower operands to HIR. We use dummy register classes if an error
137         // occurs during lowering because we still need to be able to produce a
138         // valid HIR.
139         let sess = self.tcx.sess;
140         let mut operands: Vec<_> = asm
141             .operands
142             .iter()
143             .map(|(op, op_sp)| {
144                 let lower_reg = |reg| match reg {
145                     InlineAsmRegOrRegClass::Reg(s) => {
146                         asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
147                             asm::InlineAsmReg::parse(asm_arch, s).unwrap_or_else(|e| {
148                                 let msg = format!("invalid register `{}`: {}", s, e);
149                                 sess.struct_span_err(*op_sp, &msg).emit();
150                                 asm::InlineAsmReg::Err
151                             })
152                         } else {
153                             asm::InlineAsmReg::Err
154                         })
155                     }
156                     InlineAsmRegOrRegClass::RegClass(s) => {
157                         asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
158                             asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
159                                 let msg = format!("invalid register class `{}`: {}", s, e);
160                                 sess.struct_span_err(*op_sp, &msg).emit();
161                                 asm::InlineAsmRegClass::Err
162                             })
163                         } else {
164                             asm::InlineAsmRegClass::Err
165                         })
166                     }
167                 };
168
169                 let op = match *op {
170                     InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
171                         reg: lower_reg(reg),
172                         expr: self.lower_expr_mut(expr),
173                     },
174                     InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
175                         reg: lower_reg(reg),
176                         late,
177                         expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
178                     },
179                     InlineAsmOperand::InOut { reg, late, ref expr } => {
180                         hir::InlineAsmOperand::InOut {
181                             reg: lower_reg(reg),
182                             late,
183                             expr: self.lower_expr_mut(expr),
184                         }
185                     }
186                     InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
187                         hir::InlineAsmOperand::SplitInOut {
188                             reg: lower_reg(reg),
189                             late,
190                             in_expr: self.lower_expr_mut(in_expr),
191                             out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
192                         }
193                     }
194                     InlineAsmOperand::Const { ref anon_const } => {
195                         if !self.tcx.features().asm_const {
196                             feature_err(
197                                 &sess.parse_sess,
198                                 sym::asm_const,
199                                 *op_sp,
200                                 "const operands for inline assembly are unstable",
201                             )
202                             .emit();
203                         }
204                         hir::InlineAsmOperand::Const {
205                             anon_const: self.lower_anon_const(anon_const),
206                         }
207                     }
208                     InlineAsmOperand::Sym { ref sym } => {
209                         if !self.tcx.features().asm_sym {
210                             feature_err(
211                                 &sess.parse_sess,
212                                 sym::asm_sym,
213                                 *op_sp,
214                                 "sym operands for inline assembly are unstable",
215                             )
216                             .emit();
217                         }
218
219                         let static_def_id = self
220                             .resolver
221                             .get_partial_res(sym.id)
222                             .filter(|res| res.unresolved_segments() == 0)
223                             .and_then(|res| {
224                                 if let Res::Def(DefKind::Static(_), def_id) = res.base_res() {
225                                     Some(def_id)
226                                 } else {
227                                     None
228                                 }
229                             });
230
231                         if let Some(def_id) = static_def_id {
232                             let path = self.lower_qpath(
233                                 sym.id,
234                                 &sym.qself,
235                                 &sym.path,
236                                 ParamMode::Optional,
237                                 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
238                             );
239                             hir::InlineAsmOperand::SymStatic { path, def_id }
240                         } else {
241                             // Replace the InlineAsmSym AST node with an
242                             // Expr using the name node id.
243                             let expr = Expr {
244                                 id: sym.id,
245                                 kind: ExprKind::Path(sym.qself.clone(), sym.path.clone()),
246                                 span: *op_sp,
247                                 attrs: AttrVec::new(),
248                                 tokens: None,
249                             };
250
251                             // Wrap the expression in an AnonConst.
252                             let parent_def_id = self.current_hir_id_owner;
253                             let node_id = self.next_node_id();
254                             self.create_def(parent_def_id, node_id, DefPathData::AnonConst);
255                             let anon_const = AnonConst { id: node_id, value: P(expr) };
256                             hir::InlineAsmOperand::SymFn {
257                                 anon_const: self.lower_anon_const(&anon_const),
258                             }
259                         }
260                     }
261                 };
262                 (op, self.lower_span(*op_sp))
263             })
264             .collect();
265
266         // Validate template modifiers against the register classes for the operands
267         for p in &asm.template {
268             if let InlineAsmTemplatePiece::Placeholder {
269                 operand_idx,
270                 modifier: Some(modifier),
271                 span: placeholder_span,
272             } = *p
273             {
274                 let op_sp = asm.operands[operand_idx].1;
275                 match &operands[operand_idx].0 {
276                     hir::InlineAsmOperand::In { reg, .. }
277                     | hir::InlineAsmOperand::Out { reg, .. }
278                     | hir::InlineAsmOperand::InOut { reg, .. }
279                     | hir::InlineAsmOperand::SplitInOut { reg, .. } => {
280                         let class = reg.reg_class();
281                         if class == asm::InlineAsmRegClass::Err {
282                             continue;
283                         }
284                         let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
285                         if !valid_modifiers.contains(&modifier) {
286                             let mut err = sess.struct_span_err(
287                                 placeholder_span,
288                                 "invalid asm template modifier for this register class",
289                             );
290                             err.span_label(placeholder_span, "template modifier");
291                             err.span_label(op_sp, "argument");
292                             if !valid_modifiers.is_empty() {
293                                 let mut mods = format!("`{}`", valid_modifiers[0]);
294                                 for m in &valid_modifiers[1..] {
295                                     let _ = write!(mods, ", `{}`", m);
296                                 }
297                                 err.note(&format!(
298                                     "the `{}` register class supports \
299                                      the following template modifiers: {}",
300                                     class.name(),
301                                     mods
302                                 ));
303                             } else {
304                                 err.note(&format!(
305                                     "the `{}` register class does not support template modifiers",
306                                     class.name()
307                                 ));
308                             }
309                             err.emit();
310                         }
311                     }
312                     hir::InlineAsmOperand::Const { .. } => {
313                         let mut err = sess.struct_span_err(
314                             placeholder_span,
315                             "asm template modifiers are not allowed for `const` arguments",
316                         );
317                         err.span_label(placeholder_span, "template modifier");
318                         err.span_label(op_sp, "argument");
319                         err.emit();
320                     }
321                     hir::InlineAsmOperand::SymFn { .. }
322                     | hir::InlineAsmOperand::SymStatic { .. } => {
323                         let mut err = sess.struct_span_err(
324                             placeholder_span,
325                             "asm template modifiers are not allowed for `sym` arguments",
326                         );
327                         err.span_label(placeholder_span, "template modifier");
328                         err.span_label(op_sp, "argument");
329                         err.emit();
330                     }
331                 }
332             }
333         }
334
335         let mut used_input_regs = FxHashMap::default();
336         let mut used_output_regs = FxHashMap::default();
337
338         for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
339             if let Some(reg) = op.reg() {
340                 let reg_class = reg.reg_class();
341                 if reg_class == asm::InlineAsmRegClass::Err {
342                     continue;
343                 }
344
345                 // Some register classes can only be used as clobbers. This
346                 // means that we disallow passing a value in/out of the asm and
347                 // require that the operand name an explicit register, not a
348                 // register class.
349                 if reg_class.is_clobber_only(asm_arch.unwrap()) && !op.is_clobber() {
350                     let msg = format!(
351                         "register class `{}` can only be used as a clobber, \
352                              not as an input or output",
353                         reg_class.name()
354                     );
355                     sess.struct_span_err(op_sp, &msg).emit();
356                     continue;
357                 }
358
359                 // Check for conflicts between explicit register operands.
360                 if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
361                     let (input, output) = match op {
362                         hir::InlineAsmOperand::In { .. } => (true, false),
363
364                         // Late output do not conflict with inputs, but normal outputs do
365                         hir::InlineAsmOperand::Out { late, .. } => (!late, true),
366
367                         hir::InlineAsmOperand::InOut { .. }
368                         | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
369
370                         hir::InlineAsmOperand::Const { .. }
371                         | hir::InlineAsmOperand::SymFn { .. }
372                         | hir::InlineAsmOperand::SymStatic { .. } => {
373                             unreachable!()
374                         }
375                     };
376
377                     // Flag to output the error only once per operand
378                     let mut skip = false;
379                     reg.overlapping_regs(|r| {
380                         let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
381                                          input| {
382                             match used_regs.entry(r) {
383                                 Entry::Occupied(o) => {
384                                     if skip {
385                                         return;
386                                     }
387                                     skip = true;
388
389                                     let idx2 = *o.get();
390                                     let &(ref op2, op_sp2) = &operands[idx2];
391                                     let Some(asm::InlineAsmRegOrRegClass::Reg(reg2)) = op2.reg() else {
392                                         unreachable!();
393                                     };
394
395                                     let msg = format!(
396                                         "register `{}` conflicts with register `{}`",
397                                         reg.name(),
398                                         reg2.name()
399                                     );
400                                     let mut err = sess.struct_span_err(op_sp, &msg);
401                                     err.span_label(op_sp, &format!("register `{}`", reg.name()));
402                                     err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
403
404                                     match (op, op2) {
405                                         (
406                                             hir::InlineAsmOperand::In { .. },
407                                             hir::InlineAsmOperand::Out { late, .. },
408                                         )
409                                         | (
410                                             hir::InlineAsmOperand::Out { late, .. },
411                                             hir::InlineAsmOperand::In { .. },
412                                         ) => {
413                                             assert!(!*late);
414                                             let out_op_sp = if input { op_sp2 } else { op_sp };
415                                             let msg = "use `lateout` instead of \
416                                                        `out` to avoid conflict";
417                                             err.span_help(out_op_sp, msg);
418                                         }
419                                         _ => {}
420                                     }
421
422                                     err.emit();
423                                 }
424                                 Entry::Vacant(v) => {
425                                     if r == reg {
426                                         v.insert(idx);
427                                     }
428                                 }
429                             }
430                         };
431                         if input {
432                             check(&mut used_input_regs, true);
433                         }
434                         if output {
435                             check(&mut used_output_regs, false);
436                         }
437                     });
438                 }
439             }
440         }
441
442         // If a clobber_abi is specified, add the necessary clobbers to the
443         // operands list.
444         let mut clobbered = FxHashSet::default();
445         for (abi, (_, abi_span)) in clobber_abis {
446             for &clobber in abi.clobbered_regs() {
447                 // Don't emit a clobber for a register already clobbered
448                 if clobbered.contains(&clobber) {
449                     continue;
450                 }
451
452                 let mut output_used = false;
453                 clobber.overlapping_regs(|reg| {
454                     if used_output_regs.contains_key(&reg) {
455                         output_used = true;
456                     }
457                 });
458
459                 if !output_used {
460                     operands.push((
461                         hir::InlineAsmOperand::Out {
462                             reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
463                             late: true,
464                             expr: None,
465                         },
466                         self.lower_span(abi_span),
467                     ));
468                     clobbered.insert(clobber);
469                 }
470             }
471         }
472
473         let operands = self.arena.alloc_from_iter(operands);
474         let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
475         let template_strs = self.arena.alloc_from_iter(
476             asm.template_strs
477                 .iter()
478                 .map(|(sym, snippet, span)| (*sym, *snippet, self.lower_span(*span))),
479         );
480         let line_spans =
481             self.arena.alloc_from_iter(asm.line_spans.iter().map(|span| self.lower_span(*span)));
482         let hir_asm =
483             hir::InlineAsm { template, template_strs, operands, options: asm.options, line_spans };
484         self.arena.alloc(hir_asm)
485     }
486 }