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