compiler/rustc_ast_lowering/src/asm.rs RUST 515 lines View on github.com → Search inside
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 = |&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(&reg));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}

Code quality findings 11

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>.
warning correctness unchecked-indexing
let op_sp = asm.operands[operand_idx].1;
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>.
warning correctness unchecked-indexing
match &operands[operand_idx].0 {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
if reg_class.is_clobber_only(asm_arch.unwrap(), allow_experimental_reg)
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
if allow_experimental_reg || reg_class.is_clobber_only(asm_arch.unwrap(), true)
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>.
warning correctness unchecked-indexing
let (ref op2, op_sp2) = operands[idx2];
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>.
warning correctness unchecked-indexing
let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx];
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use rustc_ast::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
.and_then(|res| match res {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
overlapping_with.push(r);
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
overlapping_with.push(reg);

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.