Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
let op_sp = asm.operands[operand_idx].1;
1use std::collections::hash_map::Entry;23use 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;1112use 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};2324impl<'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 // Rustdoc needs to support asm! from foreign architectures: don't try31 // lowering the register constraints in this case.32 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 // Inline assembly is currently only stable for these architectures.39 // (See also compiletest's `has_asm_support`.)40 let is_stable = matches!(41 asm_arch,42 asm::InlineAsmArch::X8643 | asm::InlineAsmArch::X86_6444 | asm::InlineAsmArch::Arm45 | asm::InlineAsmArch::AArch6446 | asm::InlineAsmArch::Arm64EC47 | asm::InlineAsmArch::RiscV3248 | asm::InlineAsmArch::RiscV6449 | asm::InlineAsmArch::LoongArch3250 | asm::InlineAsmArch::LoongArch6451 | asm::InlineAsmArch::S390x52 | asm::InlineAsmArch::PowerPC53 | asm::InlineAsmArch::PowerPC6454 );55 if !is_stable56 && !self.tcx.features().asm_experimental_arch()57 && sp58 .ctxt()59 .outer_expn_data()60 .allow_internal_unstable61 .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 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 && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))76 && !self.tcx.sess.opts.actually_rustdoc77 {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 msg!("the `may_unwind` option is unstable"),86 )87 .emit();88 }8990 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 // If the abi was already in the list, emit an error101 match clobber_abis.get(&abi) {102 Some((prev_name, prev_sp)) => {103 // Multiple different abi names may actually be the same ABI104 // If the specified ABIs are not the same name, alert the user that they resolve to the same ABI105 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);108109 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 }133134 // Lower operands to HIR. We use dummy register classes if an error135 // occurs during lowering because we still need to be able to produce a136 // valid HIR.137 let sess = self.tcx.sess;138 let mut operands: Vec<_> = asm139 .operands140 .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::Err152 })153 } else {154 asm::InlineAsmReg::Err155 })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_classes165 .to_vec()166 .into(),167 });168 asm::InlineAsmRegClass::Err169 },170 )171 } else {172 asm::InlineAsmRegClass::Err173 })174 }175 };176177 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 = self205 .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 });211212 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 // Replace the InlineAsmSym AST node with an225 // Expr using the name node id.226 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 };233234 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();244245 // Validate template modifiers against the register classes for the operands246 for p in &asm.template {247 if let InlineAsmTemplatePiece::Placeholder {248 operand_idx,249 modifier: Some(modifier),250 span: placeholder_span,251 } = *p252 {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 }305306 let mut used_input_regs = FxHashMap::default();307 let mut used_output_regs = FxHashMap::default();308309 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 }315316 // Some register classes can only be used as clobbers. This317 // means that we disallow passing a value in/out of the asm and318 // require that the operand name an explicit register, not a319 // register class.320 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 // always clobber-only326 self.dcx().emit_err(RegisterClassOnlyClobber {327 op_span: op_sp,328 reg_class_name: reg_class.name(),329 });330 } else {331 // clobber-only in stable332 self.tcx333 .sess334 .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 }345346 // Check for conflicts between explicit register operands.347 if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {348 let (input, output) = match op {349 hir::InlineAsmOperand::In { .. } => (true, false),350351 // Late output do not conflict with inputs, but normal outputs do352 hir::InlineAsmOperand::Out { late, .. } => (!late, true),353354 hir::InlineAsmOperand::InOut { .. }355 | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),356357 hir::InlineAsmOperand::Const { .. }358 | hir::InlineAsmOperand::SymFn { .. }359 | hir::InlineAsmOperand::SymStatic { .. }360 | hir::InlineAsmOperand::Label { .. } => {361 unreachable!("{op:?} is not a register operand");362 }363 };364365 // Flag to output the error only once per operand366 let mut skip = false;367368 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;377378 let idx2 = *o.get();379 let (ref op2, op_sp2) = operands[idx2];380381 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 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 // HIR asm doesn't preserve the original alias string of the explicit register,398 // so we have to retrieve it from AST399 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 unreachable!("{op:?} is not a register operand");406 }407 };408409 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 = 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 }439440 // If a clobber_abi is specified, add the necessary clobbers to the441 // operands list.442 let mut clobbered = FxHashSet::default();443 for (abi, (_, abi_span)) in clobber_abis {444 for &clobber in abi.clobbered_regs() {445 // Don't emit a clobber for a register already clobbered446 if clobbered.contains(&clobber) {447 continue;448 }449450 let mut overlapping_with = 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(®));456457 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 }470471 // Feature gate checking for `asm_goto_with_outputs`.472 if let Some((_, op_sp)) =473 operands.iter().find(|(op, _)| matches!(op, hir::InlineAsmOperand::Label { .. }))474 {475 // Check if an output operand is used.476 let output_operand_used = operands.iter().any(|(op, _)| {477 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 msg!("using both label and output operands for inline assembly is unstable"),490 )491 .emit();492 }493 }494495 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_strs499 .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}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.