rust-lang/rust · #160068

Preserve explicit register names in inline asm diagnostics

qaijuang · merged Sep 25, 202610 files · 195 + / 41 −
compiler/rustc_ast_lowering/src/asm.rs22 + / 21 −
@@ -140,8 +140,8 @@ impl<'hir> LoweringContext<'_, 'hir> {             .iter()             .map(|(op, op_sp)| {                 let lower_reg = |&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 => &reg.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+