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::diagnostics::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.internal_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) => hir::InlineAsmRegOrRegClass::Reg {
144 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 source_name: Some(reg),
157 },
158 InlineAsmRegOrRegClass::RegClass(reg_class) => {
159 hir::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
160 asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else(
161 |supported_register_classes| {
162 self.dcx().emit_err(InvalidRegisterClass {
163 op_span: *op_sp,
164 reg_class,
165 supported_register_classes: supported_register_classes
166 .to_vec()
167 .into(),
168 });
169 asm::InlineAsmRegClass::Err
170 },
171 )
172 } else {
173 asm::InlineAsmRegClass::Err
174 })
175 }
176 };
177
178 let op = match op {
179 InlineAsmOperand::In { reg, expr } => hir::InlineAsmOperand::In {
180 reg: lower_reg(reg),
181 expr: self.lower_expr(expr),
182 },
183 InlineAsmOperand::Out { reg, late, expr } => hir::InlineAsmOperand::Out {
184 reg: lower_reg(reg),
185 late: *late,
186 expr: expr.as_ref().map(|expr| self.lower_expr(expr)),
187 },
188 InlineAsmOperand::InOut { reg, late, expr } => hir::InlineAsmOperand::InOut {
189 reg: lower_reg(reg),
190 late: *late,
191 expr: self.lower_expr(expr),
192 },
193 InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
194 hir::InlineAsmOperand::SplitInOut {
195 reg: lower_reg(reg),
196 late: *late,
197 in_expr: self.lower_expr(in_expr),
198 out_expr: out_expr.as_ref().map(|expr| self.lower_expr(expr)),
199 }
200 }
201 InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
202 anon_const: self.lower_const_block(anon_const),
203 },
204 InlineAsmOperand::Sym { sym } => {
205 let static_def_id = self
206 .get_partial_res(sym.id)
207 .and_then(|res| res.full_res())
208 .and_then(|res| match res {
209 Res::Def(DefKind::Static { .. }, def_id) => Some(def_id),
210 _ => None,
211 });
212
213 if let Some(def_id) = static_def_id {
214 let path = self.lower_qpath(
215 sym.id,
216 &sym.qself,
217 &sym.path,
218 ParamMode::Optional,
219 AllowReturnTypeNotation::No,
220 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
221 None,
222 );
223 hir::InlineAsmOperand::SymStatic { path, def_id }
224 } else {
225 let expr = Expr {
228 id: sym.id,
229 kind: ExprKind::Path(sym.qself.clone(), sym.path.clone()),
230 span: *op_sp,
231 attrs: AttrVec::new(),
232 tokens: None,
233 };
234
235 hir::InlineAsmOperand::SymFn { expr: self.lower_expr(&expr) }
236 }
237 }
238 InlineAsmOperand::Label { block } => {
239 hir::InlineAsmOperand::Label { block: self.lower_block(block, false) }
240 }
241 };
242 (op, self.lower_span(*op_sp))
243 })
244 .collect();
245
246 for p in &asm.template {
248 if let InlineAsmTemplatePiece::Placeholder {
249 operand_idx,
250 modifier: Some(modifier),
251 span: placeholder_span,
252 } = *p
253 {
254 let op_sp = asm.operands[operand_idx].1;
255 match &operands[operand_idx].0 {
256 hir::InlineAsmOperand::In { reg, .. }
257 | hir::InlineAsmOperand::Out { reg, .. }
258 | hir::InlineAsmOperand::InOut { reg, .. }
259 | hir::InlineAsmOperand::SplitInOut { reg, .. } => {
260 let class = reg.reg_class();
261 if class == asm::InlineAsmRegClass::Err {
262 continue;
263 }
264 let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
265 if !valid_modifiers.contains(&modifier) {
266 let sub = if valid_modifiers.is_empty() {
267 InvalidAsmTemplateModifierRegClassSub::DoesNotSupportModifier {
268 class_name: class.name(),
269 }
270 } else {
271 InvalidAsmTemplateModifierRegClassSub::SupportModifier {
272 class_name: class.name(),
273 modifiers: valid_modifiers.to_vec().into(),
274 }
275 };
276 self.dcx().emit_err(InvalidAsmTemplateModifierRegClass {
277 placeholder_span,
278 op_span: op_sp,
279 modifier: modifier.to_string(),
280 sub,
281 });
282 }
283 }
284 hir::InlineAsmOperand::Const { .. } => {
285 self.dcx().emit_err(InvalidAsmTemplateModifierConst {
286 placeholder_span,
287 op_span: op_sp,
288 });
289 }
290 hir::InlineAsmOperand::SymFn { .. }
291 | hir::InlineAsmOperand::SymStatic { .. } => {
292 self.dcx().emit_err(InvalidAsmTemplateModifierSym {
293 placeholder_span,
294 op_span: op_sp,
295 });
296 }
297 hir::InlineAsmOperand::Label { .. } => {
298 self.dcx().emit_err(InvalidAsmTemplateModifierLabel {
299 placeholder_span,
300 op_span: op_sp,
301 });
302 }
303 }
304 }
305 }
306
307 let mut used_input_regs = FxHashMap::default();
308 let mut used_output_regs = FxHashMap::default();
309
310 for (idx, &(ref op, op_sp)) in operands.iter().enumerate() {
311 if let Some(reg) = op.reg() {
312 let reg_class = reg.reg_class();
313 if reg_class == asm::InlineAsmRegClass::Err {
314 continue;
315 }
316
317 if reg_class.is_clobber_only(asm_arch.unwrap(), allow_experimental_reg)
322 && !op.is_clobber()
323 {
324 if allow_experimental_reg || reg_class.is_clobber_only(asm_arch.unwrap(), true)
325 {
326 self.dcx().emit_err(RegisterClassOnlyClobber {
328 op_span: op_sp,
329 reg_class_name: reg_class.name(),
330 });
331 } else {
332 self.tcx
334 .sess
335 .create_feature_err(
336 RegisterClassOnlyClobberStable {
337 op_span: op_sp,
338 reg_class_name: reg_class.name(),
339 },
340 sym::asm_experimental_reg,
341 )
342 .emit();
343 }
344 continue;
345 }
346
347 if let hir::InlineAsmRegOrRegClass::Reg { reg, .. } = reg {
349 let (input, output) = match op {
350 hir::InlineAsmOperand::In { .. } => (true, false),
351
352 hir::InlineAsmOperand::Out { late, .. } => (!late, true),
354
355 hir::InlineAsmOperand::InOut { .. }
356 | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
357
358 hir::InlineAsmOperand::Const { .. }
359 | hir::InlineAsmOperand::SymFn { .. }
360 | hir::InlineAsmOperand::SymStatic { .. }
361 | hir::InlineAsmOperand::Label { .. } => {
362 {
::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");
363 }
364 };
365
366 let mut skip = false;
368
369 let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
370 input,
371 r: asm::InlineAsmReg| {
372 match used_regs.entry(r) {
373 Entry::Occupied(o) => {
374 if skip {
375 return;
376 }
377 skip = true;
378
379 let idx2 = *o.get();
380 let (ref op2, op_sp2) = operands[idx2];
381
382 let in_out = match (op, op2) {
383 (
384 hir::InlineAsmOperand::In { .. },
385 hir::InlineAsmOperand::Out { late, .. },
386 )
387 | (
388 hir::InlineAsmOperand::Out { late, .. },
389 hir::InlineAsmOperand::In { .. },
390 ) => {
391 if !!*late { ::core::panicking::panic("assertion failed: !*late") };assert!(!*late);
392 let out_op_sp = if input { op_sp2 } else { op_sp };
393 Some(out_op_sp)
394 }
395 _ => None,
396 };
397 let reg1_name = op
398 .reg()
399 .and_then(|reg| reg.source_name())
400 .expect("{op:?} has no source register name");
401
402 let reg2_name = op2
403 .reg()
404 .and_then(|reg| reg.source_name())
405 .expect("{op2:?} has no source register name");
406
407 self.dcx().emit_err(RegisterConflict {
408 op_span1: op_sp,
409 op_span2: op_sp2,
410 reg1_name: reg1_name.as_str(),
411 reg2_name: reg2_name.as_str(),
412 in_out,
413 });
414 }
415 Entry::Vacant(v) => {
416 if r == reg {
417 v.insert(idx);
418 }
419 }
420 }
421 };
422 let mut overlapping_with = ::alloc::vec::Vec::new()vec![];
423 reg.overlapping_regs(|r| {
424 overlapping_with.push(r);
425 });
426 for r in overlapping_with {
427 if input {
428 check(&mut used_input_regs, true, r);
429 }
430 if output {
431 check(&mut used_output_regs, false, r);
432 }
433 }
434 }
435 }
436 }
437
438 let mut clobbered = FxHashSet::default();
441 for (abi, (_, abi_span)) in clobber_abis {
442 for &clobber in abi.clobbered_regs() {
443 if clobbered.contains(&clobber) {
445 continue;
446 }
447
448 let mut overlapping_with = ::alloc::vec::Vec::new()vec![];
449 clobber.overlapping_regs(|reg| {
450 overlapping_with.push(reg);
451 });
452 let output_used =
453 overlapping_with.iter().any(|reg| used_output_regs.contains_key(®));
454
455 if !output_used {
456 operands.push((
457 hir::InlineAsmOperand::Out {
458 reg: hir::InlineAsmRegOrRegClass::Reg {
459 reg: clobber,
460 source_name: None,
461 },
462 late: true,
463 expr: None,
464 },
465 self.lower_span(abi_span),
466 ));
467 clobbered.insert(clobber);
468 }
469 }
470 }
471
472 if let Some((_, op_sp)) =
474 operands.iter().find(|(op, _)| #[allow(non_exhaustive_omitted_patterns)] match op {
hir::InlineAsmOperand::Label { .. } => true,
_ => false,
}matches!(op, hir::InlineAsmOperand::Label { .. }))
475 {
476 let output_operand_used = operands.iter().any(|(op, _)| {
478 #[allow(non_exhaustive_omitted_patterns)] match op {
hir::InlineAsmOperand::Out { expr: Some(_), .. } |
hir::InlineAsmOperand::InOut { .. } |
hir::InlineAsmOperand::SplitInOut { out_expr: Some(_), .. } => true,
_ => false,
}matches!(
479 op,
480 hir::InlineAsmOperand::Out { expr: Some(_), .. }
481 | hir::InlineAsmOperand::InOut { .. }
482 | hir::InlineAsmOperand::SplitInOut { out_expr: Some(_), .. }
483 )
484 });
485 if output_operand_used && !self.tcx.features().asm_goto_with_outputs() {
486 feature_err(
487 sess,
488 sym::asm_goto_with_outputs,
489 *op_sp,
490 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"),
491 )
492 .emit();
493 }
494 }
495
496 let operands = self.arena.alloc_from_iter(operands);
497 let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
498 let template_strs = self.arena.alloc_from_iter(
499 asm.template_strs
500 .iter()
501 .map(|(sym, snippet, span)| (*sym, *snippet, self.lower_span(*span))),
502 );
503 let line_spans =
504 self.arena.alloc_from_iter(asm.line_spans.iter().map(|span| self.lower_span(*span)));
505 let hir_asm = hir::InlineAsm {
506 asm_macro: asm.asm_macro,
507 template,
508 template_strs,
509 operands,
510 options: asm.options,
511 line_spans,
512 };
513 self.arena.alloc(hir_asm)
514 }
515}