1use std::collections::hash_map::Entry;
2
3use rustc_ast::*;
4use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
5use rustc_errors::msg;
6use rustc_hir as hir;
7use rustc_hir::def::{DefKind, Res};
8use rustc_session::errors::feature_err;
9use rustc_span::{Span, sym};
10use rustc_target::asm;
11
12use crate::diagnostics::{
13 AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported,
14 InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst,
15 InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass,
16 InvalidAsmTemplateModifierRegClassSub, InvalidAsmTemplateModifierSym, InvalidRegister,
17 InvalidRegisterClass, RegisterClassOnlyClobber, RegisterClassOnlyClobberStable,
18 RegisterConflict,
19};
20use crate::{
21 AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
22};
23
24impl<'hir> LoweringContext<'_, 'hir> {
25 pub(crate) fn lower_inline_asm(
26 &mut self,
27 sp: Span,
28 asm: &InlineAsm,
29 ) -> &'hir hir::InlineAsm<'hir> {
30 let asm_arch =
33 if self.tcx.sess.opts.actually_rustdoc { None } else { self.tcx.sess.asm_arch };
34 if asm_arch.is_none() && !self.tcx.sess.opts.actually_rustdoc {
35 self.dcx().emit_err(InlineAsmUnsupportedTarget { span: sp });
36 }
37 if let Some(asm_arch) = asm_arch {
38 let is_stable = #[allow(non_exhaustive_omitted_patterns)] match asm_arch {
asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64 |
asm::InlineAsmArch::Arm | asm::InlineAsmArch::AArch64 |
asm::InlineAsmArch::Arm64EC | asm::InlineAsmArch::RiscV32 |
asm::InlineAsmArch::RiscV64 | asm::InlineAsmArch::LoongArch32 |
asm::InlineAsmArch::LoongArch64 | asm::InlineAsmArch::S390x |
asm::InlineAsmArch::PowerPC | asm::InlineAsmArch::PowerPC64 => true,
_ => false,
}matches!(
41 asm_arch,
42 asm::InlineAsmArch::X86
43 | asm::InlineAsmArch::X86_64
44 | asm::InlineAsmArch::Arm
45 | asm::InlineAsmArch::AArch64
46 | asm::InlineAsmArch::Arm64EC
47 | asm::InlineAsmArch::RiscV32
48 | asm::InlineAsmArch::RiscV64
49 | asm::InlineAsmArch::LoongArch32
50 | asm::InlineAsmArch::LoongArch64
51 | asm::InlineAsmArch::S390x
52 | asm::InlineAsmArch::PowerPC
53 | asm::InlineAsmArch::PowerPC64
54 );
55 if !is_stable
56 && !self.tcx.features().asm_experimental_arch()
57 && sp
58 .ctxt()
59 .outer_expn_data()
60 .allow_internal_unstable
61 .filter(|features| features.contains(&sym::asm_experimental_arch))
62 .is_none()
63 {
64 feature_err(
65 &self.tcx.sess,
66 sym::asm_experimental_arch,
67 sp,
68 rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("inline assembly is not stable yet on this architecture"))msg!("inline assembly is not stable yet on this architecture"),
69 )
70 .emit();
71 }
72 }
73 let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
74 if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
75 && !#[allow(non_exhaustive_omitted_patterns)] match asm_arch {
Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64) => true,
_ => false,
}matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
76 && !self.tcx.sess.opts.actually_rustdoc
77 {
78 self.dcx().emit_err(AttSyntaxOnlyX86 { span: sp });
79 }
80 if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind() {
81 feature_err(
82 &self.tcx.sess,
83 sym::asm_unwind,
84 sp,
85 rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the `may_unwind` option is unstable"))msg!("the `may_unwind` option is unstable"),
86 )
87 .emit();
88 }
89
90 let mut clobber_abis = FxIndexMap::default();
91 if let Some(asm_arch) = asm_arch {
92 for (abi_name, abi_span) in &asm.clobber_abis {
93 match asm::InlineAsmClobberAbi::parse(
94 asm_arch,
95 &self.tcx.sess.target,
96 &self.tcx.sess.unstable_target_features,
97 *abi_name,
98 ) {
99 Ok(abi) => {
100 match clobber_abis.get(&abi) {
102 Some((prev_name, prev_sp)) => {
103 let source_map = self.tcx.sess.source_map();
106 let equivalent = source_map.span_to_snippet(*prev_sp)
107 != source_map.span_to_snippet(*abi_span);
108
109 self.dcx().emit_err(AbiSpecifiedMultipleTimes {
110 abi_span: *abi_span,
111 prev_name: *prev_name,
112 prev_span: *prev_sp,
113 equivalent,
114 });
115 }
116 None => {
117 clobber_abis.insert(abi, (*abi_name, *abi_span));
118 }
119 }
120 }
121 Err(&[]) => {
122 self.dcx().emit_err(ClobberAbiNotSupported { abi_span: *abi_span });
123 }
124 Err(supported_abis) => {
125 self.dcx().emit_err(InvalidAbiClobberAbi {
126 abi_span: *abi_span,
127 supported_abis: supported_abis.to_vec().into(),
128 });
129 }
130 }
131 }
132 }
133
134 let sess = self.tcx.sess;
138 let mut operands: Vec<_> = asm
139 .operands
140 .iter()
141 .map(|(op, op_sp)| {
142 let lower_reg = |®: &_| match reg {
143 InlineAsmRegOrRegClass::Reg(reg) => {
144 asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
145 asm::InlineAsmReg::parse(asm_arch, reg).unwrap_or_else(|error| {
146 self.dcx().emit_err(InvalidRegister {
147 op_span: *op_sp,
148 reg,
149 error,
150 });
151 asm::InlineAsmReg::Err
152 })
153 } else {
154 asm::InlineAsmReg::Err
155 })
156 }
157 InlineAsmRegOrRegClass::RegClass(reg_class) => {
158 asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
159 asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else(
160 |supported_register_classes| {
161 self.dcx().emit_err(InvalidRegisterClass {
162 op_span: *op_sp,
163 reg_class,
164 supported_register_classes: supported_register_classes
165 .to_vec()
166 .into(),
167 });
168 asm::InlineAsmRegClass::Err
169 },
170 )
171 } else {
172 asm::InlineAsmRegClass::Err
173 })
174 }
175 };
176
177 let op = match op {
178 InlineAsmOperand::In { reg, expr } => hir::InlineAsmOperand::In {
179 reg: lower_reg(reg),
180 expr: self.lower_expr(expr),
181 },
182 InlineAsmOperand::Out { reg, late, expr } => hir::InlineAsmOperand::Out {
183 reg: lower_reg(reg),
184 late: *late,
185 expr: expr.as_ref().map(|expr| self.lower_expr(expr)),
186 },
187 InlineAsmOperand::InOut { reg, late, expr } => hir::InlineAsmOperand::InOut {
188 reg: lower_reg(reg),
189 late: *late,
190 expr: self.lower_expr(expr),
191 },
192 InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
193 hir::InlineAsmOperand::SplitInOut {
194 reg: lower_reg(reg),
195 late: *late,
196 in_expr: self.lower_expr(in_expr),
197 out_expr: out_expr.as_ref().map(|expr| self.lower_expr(expr)),
198 }
199 }
200 InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
201 anon_const: self.lower_const_block(anon_const),
202 },
203 InlineAsmOperand::Sym { sym } => {
204 let static_def_id = self
205 .get_partial_res(sym.id)
206 .and_then(|res| res.full_res())
207 .and_then(|res| match res {
208 Res::Def(DefKind::Static { .. }, def_id) => Some(def_id),
209 _ => None,
210 });
211
212 if let Some(def_id) = static_def_id {
213 let path = self.lower_qpath(
214 sym.id,
215 &sym.qself,
216 &sym.path,
217 ParamMode::Optional,
218 AllowReturnTypeNotation::No,
219 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
220 None,
221 );
222 hir::InlineAsmOperand::SymStatic { path, def_id }
223 } else {
224 let expr = Expr {
227 id: sym.id,
228 kind: ExprKind::Path(sym.qself.clone(), sym.path.clone()),
229 span: *op_sp,
230 attrs: AttrVec::new(),
231 tokens: None,
232 };
233
234 hir::InlineAsmOperand::SymFn { expr: self.lower_expr(&expr) }
235 }
236 }
237 InlineAsmOperand::Label { block } => {
238 hir::InlineAsmOperand::Label { block: self.lower_block(block, false) }
239 }
240 };
241 (op, self.lower_span(*op_sp))
242 })
243 .collect();
244
245 for p in &asm.template {
247 if let InlineAsmTemplatePiece::Placeholder {
248 operand_idx,
249 modifier: Some(modifier),
250 span: placeholder_span,
251 } = *p
252 {
253 let op_sp = asm.operands[operand_idx].1;
254 match &operands[operand_idx].0 {
255 hir::InlineAsmOperand::In { reg, .. }
256 | hir::InlineAsmOperand::Out { reg, .. }
257 | hir::InlineAsmOperand::InOut { reg, .. }
258 | hir::InlineAsmOperand::SplitInOut { reg, .. } => {
259 let class = reg.reg_class();
260 if class == asm::InlineAsmRegClass::Err {
261 continue;
262 }
263 let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
264 if !valid_modifiers.contains(&modifier) {
265 let sub = if valid_modifiers.is_empty() {
266 InvalidAsmTemplateModifierRegClassSub::DoesNotSupportModifier {
267 class_name: class.name(),
268 }
269 } else {
270 InvalidAsmTemplateModifierRegClassSub::SupportModifier {
271 class_name: class.name(),
272 modifiers: valid_modifiers.to_vec().into(),
273 }
274 };
275 self.dcx().emit_err(InvalidAsmTemplateModifierRegClass {
276 placeholder_span,
277 op_span: op_sp,
278 modifier: modifier.to_string(),
279 sub,
280 });
281 }
282 }
283 hir::InlineAsmOperand::Const { .. } => {
284 self.dcx().emit_err(InvalidAsmTemplateModifierConst {
285 placeholder_span,
286 op_span: op_sp,
287 });
288 }
289 hir::InlineAsmOperand::SymFn { .. }
290 | hir::InlineAsmOperand::SymStatic { .. } => {
291 self.dcx().emit_err(InvalidAsmTemplateModifierSym {
292 placeholder_span,
293 op_span: op_sp,
294 });
295 }
296 hir::InlineAsmOperand::Label { .. } => {
297 self.dcx().emit_err(InvalidAsmTemplateModifierLabel {
298 placeholder_span,
299 op_span: op_sp,
300 });
301 }
302 }
303 }
304 }
305
306 let mut used_input_regs = FxHashMap::default();
307 let mut used_output_regs = FxHashMap::default();
308
309 for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
310 if let Some(reg) = op.reg() {
311 let reg_class = reg.reg_class();
312 if reg_class == asm::InlineAsmRegClass::Err {
313 continue;
314 }
315
316 if reg_class.is_clobber_only(asm_arch.unwrap(), allow_experimental_reg)
321 && !op.is_clobber()
322 {
323 if allow_experimental_reg || reg_class.is_clobber_only(asm_arch.unwrap(), true)
324 {
325 self.dcx().emit_err(RegisterClassOnlyClobber {
327 op_span: op_sp,
328 reg_class_name: reg_class.name(),
329 });
330 } else {
331 self.tcx
333 .sess
334 .create_feature_err(
335 RegisterClassOnlyClobberStable {
336 op_span: op_sp,
337 reg_class_name: reg_class.name(),
338 },
339 sym::asm_experimental_reg,
340 )
341 .emit();
342 }
343 continue;
344 }
345
346 if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
348 let (input, output) = match op {
349 hir::InlineAsmOperand::In { .. } => (true, false),
350
351 hir::InlineAsmOperand::Out { late, .. } => (!late, true),
353
354 hir::InlineAsmOperand::InOut { .. }
355 | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
356
357 hir::InlineAsmOperand::Const { .. }
358 | hir::InlineAsmOperand::SymFn { .. }
359 | hir::InlineAsmOperand::SymStatic { .. }
360 | hir::InlineAsmOperand::Label { .. } => {
361 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("{0:?} is not a register operand", op)));
};unreachable!("{op:?} is not a register operand");
362 }
363 };
364
365 let mut skip = false;
367
368 let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
369 input,
370 r: asm::InlineAsmReg| {
371 match used_regs.entry(r) {
372 Entry::Occupied(o) => {
373 if skip {
374 return;
375 }
376 skip = true;
377
378 let idx2 = *o.get();
379 let (ref op2, op_sp2) = operands[idx2];
380
381 let in_out = match (op, op2) {
382 (
383 hir::InlineAsmOperand::In { .. },
384 hir::InlineAsmOperand::Out { late, .. },
385 )
386 | (
387 hir::InlineAsmOperand::Out { late, .. },
388 hir::InlineAsmOperand::In { .. },
389 ) => {
390 if !!*late { ::core::panicking::panic("assertion failed: !*late") };assert!(!*late);
391 let out_op_sp = if input { op_sp2 } else { op_sp };
392 Some(out_op_sp)
393 }
394 _ => None,
395 };
396 let reg_str = |idx| -> &str {
397 let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx];
400 if let Some(ast::InlineAsmRegOrRegClass::Reg(reg_sym)) =
401 op.reg()
402 {
403 reg_sym.as_str()
404 } else {
405 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("{0:?} is not a register operand", op)));
};unreachable!("{op:?} is not a register operand");
406 }
407 };
408
409 self.dcx().emit_err(RegisterConflict {
410 op_span1: op_sp,
411 op_span2: op_sp2,
412 reg1_name: reg_str(idx),
413 reg2_name: reg_str(idx2),
414 in_out,
415 });
416 }
417 Entry::Vacant(v) => {
418 if r == reg {
419 v.insert(idx);
420 }
421 }
422 }
423 };
424 let mut overlapping_with = ::alloc::vec::Vec::new()vec![];
425 reg.overlapping_regs(|r| {
426 overlapping_with.push(r);
427 });
428 for r in overlapping_with {
429 if input {
430 check(&mut used_input_regs, true, r);
431 }
432 if output {
433 check(&mut used_output_regs, false, r);
434 }
435 }
436 }
437 }
438 }
439
440 let mut clobbered = FxHashSet::default();
443 for (abi, (_, abi_span)) in clobber_abis {
444 for &clobber in abi.clobbered_regs() {
445 if clobbered.contains(&clobber) {
447 continue;
448 }
449
450 let mut overlapping_with = ::alloc::vec::Vec::new()vec![];
451 clobber.overlapping_regs(|reg| {
452 overlapping_with.push(reg);
453 });
454 let output_used =
455 overlapping_with.iter().any(|reg| used_output_regs.contains_key(®));
456
457 if !output_used {
458 operands.push((
459 hir::InlineAsmOperand::Out {
460 reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
461 late: true,
462 expr: None,
463 },
464 self.lower_span(abi_span),
465 ));
466 clobbered.insert(clobber);
467 }
468 }
469 }
470
471 if let Some((_, op_sp)) =
473 operands.iter().find(|(op, _)| #[allow(non_exhaustive_omitted_patterns)] match op {
hir::InlineAsmOperand::Label { .. } => true,
_ => false,
}matches!(op, hir::InlineAsmOperand::Label { .. }))
474 {
475 let output_operand_used = operands.iter().any(|(op, _)| {
477 #[allow(non_exhaustive_omitted_patterns)] match op {
hir::InlineAsmOperand::Out { expr: Some(_), .. } |
hir::InlineAsmOperand::InOut { .. } |
hir::InlineAsmOperand::SplitInOut { out_expr: Some(_), .. } => true,
_ => false,
}matches!(
478 op,
479 hir::InlineAsmOperand::Out { expr: Some(_), .. }
480 | hir::InlineAsmOperand::InOut { .. }
481 | hir::InlineAsmOperand::SplitInOut { out_expr: Some(_), .. }
482 )
483 });
484 if output_operand_used && !self.tcx.features().asm_goto_with_outputs() {
485 feature_err(
486 sess,
487 sym::asm_goto_with_outputs,
488 *op_sp,
489 rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("using both label and output operands for inline assembly is unstable"))msg!("using both label and output operands for inline assembly is unstable"),
490 )
491 .emit();
492 }
493 }
494
495 let operands = self.arena.alloc_from_iter(operands);
496 let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
497 let template_strs = self.arena.alloc_from_iter(
498 asm.template_strs
499 .iter()
500 .map(|(sym, snippet, span)| (*sym, *snippet, self.lower_span(*span))),
501 );
502 let line_spans =
503 self.arena.alloc_from_iter(asm.line_spans.iter().map(|span| self.lower_span(*span)));
504 let hir_asm = hir::InlineAsm {
505 asm_macro: asm.asm_macro,
506 template,
507 template_strs,
508 operands,
509 options: asm.options,
510 line_spans,
511 };
512 self.arena.alloc(hir_asm)
513 }
514}