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