diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index fd3a00d56fe0b..71bcdb0e27e8f 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -140,8 +140,8 @@ impl<'hir> LoweringContext<'_, 'hir> { .iter() .map(|(op, op_sp)| { let lower_reg = |®: &_| match reg { - InlineAsmRegOrRegClass::Reg(reg) => { - asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { + InlineAsmRegOrRegClass::Reg(reg) => hir::InlineAsmRegOrRegClass::Reg { + reg: if let Some(asm_arch) = asm_arch { asm::InlineAsmReg::parse(asm_arch, reg).unwrap_or_else(|error| { self.dcx().emit_err(InvalidRegister { op_span: *op_sp, @@ -152,10 +152,11 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } else { asm::InlineAsmReg::Err - }) - } + }, + source_name: Some(reg), + }, InlineAsmRegOrRegClass::RegClass(reg_class) => { - asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { + hir::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else( |supported_register_classes| { self.dcx().emit_err(InvalidRegisterClass { @@ -344,7 +345,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } // Check for conflicts between explicit register operands. - if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg { + if let hir::InlineAsmRegOrRegClass::Reg { reg, .. } = reg { let (input, output) = match op { hir::InlineAsmOperand::In { .. } => (true, false), @@ -393,24 +394,21 @@ impl<'hir> LoweringContext<'_, 'hir> { } _ => None, }; - let reg_str = |idx| -> &str { - // HIR asm doesn't preserve the original alias string of the explicit register, - // so we have to retrieve it from AST - let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx]; - if let Some(ast::InlineAsmRegOrRegClass::Reg(reg_sym)) = - op.reg() - { - reg_sym.as_str() - } else { - unreachable!("{op:?} is not a register operand"); - } - }; + let reg1_name = op + .reg() + .and_then(|reg| reg.source_name()) + .expect("{op:?} has no source register name"); + + let reg2_name = op2 + .reg() + .and_then(|reg| reg.source_name()) + .expect("{op2:?} has no source register name"); self.dcx().emit_err(RegisterConflict { op_span1: op_sp, op_span2: op_sp2, - reg1_name: reg_str(idx), - reg2_name: reg_str(idx2), + reg1_name: reg1_name.as_str(), + reg2_name: reg2_name.as_str(), in_out, }); } @@ -457,7 +455,10 @@ impl<'hir> LoweringContext<'_, 'hir> { if !output_used { operands.push(( hir::InlineAsmOperand::Out { - reg: asm::InlineAsmRegOrRegClass::Reg(clobber), + reg: hir::InlineAsmRegOrRegClass::Reg { + reg: clobber, + source_name: None, + }, late: true, expr: None, }, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ee79680d7d1e9..7df199c8cea7b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -30,7 +30,7 @@ use rustc_span::{ BytePos, DUMMY_SP, DesugaringKind, ErrorGuaranteed, Ident, LocalExpnId, Span, Spanned, Symbol, kw, sym, }; -use rustc_target::asm::InlineAsmRegOrRegClass; +use rustc_target::asm; use tracing::debug; use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res}; @@ -3611,6 +3611,44 @@ pub enum TyKind<'hir, Unambig = ()> { Infer(Unambig), } +/// Stores explicit register name from source +/// for diagnostics only. +#[derive(Debug, Clone, Copy, StableHash)] +pub enum InlineAsmRegOrRegClass { + Reg { reg: asm::InlineAsmReg, source_name: Option }, + RegClass(asm::InlineAsmRegClass), +} + +impl InlineAsmRegOrRegClass { + // For `rustc_mir_build` and `clippy_utils` + pub fn as_target(self) -> asm::InlineAsmRegOrRegClass { + match self { + Self::Reg { reg, .. } => asm::InlineAsmRegOrRegClass::Reg(reg), + Self::RegClass(reg_class) => asm::InlineAsmRegOrRegClass::RegClass(reg_class), + } + } + + // For `rustc_ast_lowering` + pub fn reg_class(self) -> asm::InlineAsmRegClass { + self.as_target().reg_class() + } + + // For `rustc_ast_lowering` + pub fn source_name(self) -> Option { + match self { + Self::Reg { source_name, .. } => source_name, + Self::RegClass(_) => None, + } + } +} + +// For `rustc_hir_pretty` +impl fmt::Display for InlineAsmRegOrRegClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_target().fmt(f) + } +} + #[derive(Debug, Clone, Copy, StableHash)] pub enum InlineAsmOperand<'hir> { In { @@ -3665,7 +3703,7 @@ impl<'hir> InlineAsmOperand<'hir> { pub fn is_clobber(&self) -> bool { matches!( self, - InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None } + InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg { .. }, expr: None, .. } ) } } diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 2b1d9ed038b87..4790159f4eca7 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -12,8 +12,7 @@ use rustc_middle::ty::{ use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span, Symbol, bug, sym}; use rustc_target::asm::{ - InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmSize, InlineAsmType, - ModifierInfo, + InlineAsmReg, InlineAsmRegClass, InlineAsmSize, InlineAsmType, ModifierInfo, }; use rustc_trait_selection::infer::InferCtxtExt; @@ -190,7 +189,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { fn check_asm_operand_type( &self, idx: usize, - reg: InlineAsmRegOrRegClass, + reg: hir::InlineAsmRegOrRegClass, expr: &'tcx hir::Expr<'tcx>, template: &[InlineAsmTemplatePiece], is_input: bool, @@ -476,7 +475,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if let Some(reg) = op.reg() { // Some explicit registers cannot be used depending on the // target. Reject those here. - if let InlineAsmRegOrRegClass::Reg(reg) = reg { + if let hir::InlineAsmRegOrRegClass::Reg { reg, source_name } = reg { if let InlineAsmReg::Err = reg { // `validate` will panic on `Err`, as an error must // already have been reported. @@ -489,7 +488,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { &self.tcx().sess.target, op.is_clobber(), ) { - let msg = format!("cannot use register `{}`: {}", reg.name(), msg); + let reg_name = match source_name { + Some(ref reg_name) => reg_name.as_str(), + None => ®.name(), + }; + let msg = format!("cannot use register `{reg_name}`: {msg}"); self.fcx.dcx().span_err(op_sp, msg); continue; } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index feef8d24bd9ca..5bad7bec531c9 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -827,22 +827,27 @@ impl<'tcx> ThirBuildCx<'tcx> { .operands .iter() .map(|(op, _op_sp)| match *op { - hir::InlineAsmOperand::In { reg, expr } => { - InlineAsmOperand::In { reg, expr: self.mirror_expr(expr) } - } + hir::InlineAsmOperand::In { reg, expr } => InlineAsmOperand::In { + reg: reg.as_target(), + expr: self.mirror_expr(expr), + }, hir::InlineAsmOperand::Out { reg, late, ref expr } => { InlineAsmOperand::Out { - reg, + reg: reg.as_target(), late, expr: expr.map(|expr| self.mirror_expr(expr)), } } hir::InlineAsmOperand::InOut { reg, late, expr } => { - InlineAsmOperand::InOut { reg, late, expr: self.mirror_expr(expr) } + InlineAsmOperand::InOut { + reg: reg.as_target(), + late, + expr: self.mirror_expr(expr), + } } hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, ref out_expr } => { InlineAsmOperand::SplitInOut { - reg, + reg: reg.as_target(), late, in_expr: self.mirror_expr(in_expr), out_expr: out_expr.map(|expr| self.mirror_expr(expr)), diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index cf7777d037d9b..0d0b1efeca00b 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1275,18 +1275,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { for (op, _op_sp) in asm.operands { match op { InlineAsmOperand::In { reg, expr } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); self.hash_expr(expr); }, InlineAsmOperand::Out { reg, late, expr } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); late.hash(&mut self.s); if let Some(expr) = expr { self.hash_expr(expr); } }, InlineAsmOperand::InOut { reg, late, expr } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); late.hash(&mut self.s); self.hash_expr(expr); }, @@ -1296,7 +1296,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { in_expr, out_expr, } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); late.hash(&mut self.s); self.hash_expr(in_expr); if let Some(out_expr) = out_expr { diff --git a/tests/ui/asm/sparc/bad-reg.rs b/tests/ui/asm/sparc/bad-reg.rs index 8cac5eb6b4ad0..74c31279656d1 100644 --- a/tests/ui/asm/sparc/bad-reg.rs +++ b/tests/ui/asm/sparc/bad-reg.rs @@ -28,7 +28,7 @@ fn f() { asm!("", out("g3") _); asm!("", out("g4") _); asm!("", out("g5") _); - //[sparc,sparcv8plus]~^ ERROR cannot use register `r5`: g5 is reserved for system on SPARC32 + //[sparc,sparcv8plus]~^ ERROR cannot use register `g5`: g5 is reserved for system on SPARC32 asm!("", out("g6") _); //~^ ERROR invalid register `g6`: reserved for system and cannot be used as an operand for inline asm asm!("", out("g7") _); diff --git a/tests/ui/asm/sparc/bad-reg.sparc.stderr b/tests/ui/asm/sparc/bad-reg.sparc.stderr index 06b607ff007ac..ac2ee667d4eea 100644 --- a/tests/ui/asm/sparc/bad-reg.sparc.stderr +++ b/tests/ui/asm/sparc/bad-reg.sparc.stderr @@ -64,7 +64,7 @@ error: register class `yreg` can only be used as a clobber, not as an input or o LL | asm!("/* {} */", out(yreg) _); | ^^^^^^^^^^^ -error: cannot use register `r5`: g5 is reserved for system on SPARC32 +error: cannot use register `g5`: g5 is reserved for system on SPARC32 --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("g5") _); diff --git a/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr index e0580ad3232f5..be931f9a2aad4 100644 --- a/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr +++ b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr @@ -64,7 +64,7 @@ error: register class `yreg` can only be used as a clobber, not as an input or o LL | asm!("/* {} */", out(yreg) _); | ^^^^^^^^^^^ -error: cannot use register `r5`: g5 is reserved for system on SPARC32 +error: cannot use register `g5`: g5 is reserved for system on SPARC32 --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("g5") _); diff --git a/tests/ui/asm/x86-explicit-register-alias-diagnostic.rs b/tests/ui/asm/x86-explicit-register-alias-diagnostic.rs new file mode 100644 index 0000000000000..4a67827d3c84e --- /dev/null +++ b/tests/ui/asm/x86-explicit-register-alias-diagnostic.rs @@ -0,0 +1,51 @@ +// Check that inline assembly diagnostics use the explicit register name from the source. +// +// On 32-bit x86, vector registers 8 and higher are unavailable. Registers 16 through 31 +// share a canonical `zmm` representation, but diagnostics must still use the `xmm`, `ymm`, +// or `zmm` name that the user wrote. +// +// Regression test for . + +//@ add-minicore +//@ revisions: x86 x86_64 +//@[x86] compile-flags: --target i686-unknown-linux-gnu +//@[x86] needs-llvm-components: x86 +//@[x86] check-fail +//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64] needs-llvm-components: x86 +//@[x86_64] check-pass +//@ ignore-backends: gcc + +#![feature(no_core)] +#![no_core] + +extern crate minicore; +use minicore::*; + +fn main() { + unsafe { + // Register 15 is the last register with separate canonical representations. + asm!("", lateout("xmm15") _); + //[x86]~^ ERROR cannot use register `xmm15` + asm!("", lateout("ymm15") _); + //[x86]~^ ERROR cannot use register `ymm15` + asm!("", lateout("zmm15") _); + //[x86]~^ ERROR cannot use register `zmm15` + + // Register 16 is the first register whose aliases share the canonical `zmm` representation. + asm!("", lateout("xmm16") _); + //[x86]~^ ERROR cannot use register `xmm16` + asm!("", lateout("ymm16") _); + //[x86]~^ ERROR cannot use register `ymm16` + asm!("", lateout("zmm16") _); + //[x86]~^ ERROR cannot use register `zmm16` + + // Register 31 is the upper boundary of the affected register range. + asm!("", lateout("xmm31") _); + //[x86]~^ ERROR cannot use register `xmm31` + asm!("", lateout("ymm31") _); + //[x86]~^ ERROR cannot use register `ymm31` + asm!("", lateout("zmm31") _); + //[x86]~^ ERROR cannot use register `zmm31` + } +} diff --git a/tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderr b/tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderr new file mode 100644 index 0000000000000..81718243841aa --- /dev/null +++ b/tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderr @@ -0,0 +1,56 @@ +error: cannot use register `xmm15`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:28:18 + | +LL | asm!("", lateout("xmm15") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `ymm15`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:30:18 + | +LL | asm!("", lateout("ymm15") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `zmm15`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:32:18 + | +LL | asm!("", lateout("zmm15") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `xmm16`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:36:18 + | +LL | asm!("", lateout("xmm16") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `ymm16`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:38:18 + | +LL | asm!("", lateout("ymm16") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `zmm16`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:40:18 + | +LL | asm!("", lateout("zmm16") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `xmm31`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:44:18 + | +LL | asm!("", lateout("xmm31") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `ymm31`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:46:18 + | +LL | asm!("", lateout("ymm31") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `zmm31`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:48:18 + | +LL | asm!("", lateout("zmm31") _); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors +