rust-lang/rust · #160068
Preserve explicit register names in inline asm diagnostics
compiler/rustc_ast_lowering/src/asm.rs22 + / 21 −
@@ -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, },compiler/rustc_hir/src/hir.rs40 + / 2 −
@@ -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<Symbol> },+ 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<Symbol> {+ 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, .. } ) } }compiler/rustc_hir_typeck/src/inline_asm.rs8 + / 5 −
@@ -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; }compiler/rustc_mir_build/src/thir/cx/expr.rs11 + / 6 −
@@ -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)),src/tools/clippy/clippy_utils/src/hir_utils.rs4 + / 4 −
@@ -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 {tests/ui/asm/sparc/bad-reg.rs1 + / 1 −
@@ -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") _);tests/ui/asm/sparc/bad-reg.sparc.stderr1 + / 1 −
@@ -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") _);tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr1 + / 1 −
@@ -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") _);tests/ui/asm/x86-explicit-register-alias-diagnostic.rsadded51 + / 0 −
@@ -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 <https://github.com/rust-lang/rust/issues/159409>.++//@ 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`+ }+}tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderradded56 + / 0 −
@@ -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+