Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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),

Expand Down Expand Up @@ -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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason it shouldn't, instead of this scheme of re-adding it in metadata?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see or find any reason why it shouldn't

@workingjubilee workingjubilee Aug 3, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I see how the PR looks like in that form instead, then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be duplicating the InlineAsmRegOrRegClass type and impl in HiR, is it acceptable?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exact HiR impl

/// 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)
    }
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either variants seems fine to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@workingjubilee let me know the direction we are taking.

// 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,
});
}
Expand Down Expand Up @@ -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,
},
Expand Down
42 changes: 40 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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, .. }
)
}
}
Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_hir_typeck/src/inline_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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;
}
Expand Down
17 changes: 11 additions & 6 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
8 changes: 4 additions & 4 deletions src/tools/clippy/clippy_utils/src/hir_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
},
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/asm/sparc/bad-reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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") _);
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/asm/sparc/bad-reg.sparc.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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") _);
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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") _);
Expand Down
51 changes: 51 additions & 0 deletions tests/ui/asm/x86-explicit-register-alias-diagnostic.rs
Original file line number Diff line number Diff line change
@@ -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`
}
}
56 changes: 56 additions & 0 deletions tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderr
Original file line number Diff line number Diff line change
@@ -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

Loading