mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rollup merge of #154088 - aytey:fix-asm-operand-ordering, r=davidtwco
Reorder inline asm operands in pretty printer to satisfy grammar constraints
After macro expansion, named `asm!` operands are converted to positional operands and the template string uses numeric indices. However, the pretty printer previously emitted operands in their original AST order, which could place positional (formerly named) register-class operands after explicit register operands. This violates the `asm!` grammar rule that positional arguments cannot follow explicit register arguments, causing the expanded output from `rustc -Zunpretty=expanded` to fail to reparse.
When reordering is needed, the fix partitions operands into non-explicit and explicit register groups, emits non-explicit operands first, then explicit register operands, and remaps template placeholder indices (`{N}`) to match the new positions. When operands are already correctly ordered, the original code path is used unchanged.
## Example
**before** (`rustc 1.96.0-nightly (3b1b0ef4d 2026-03-11)`):
```rust
#![feature(prelude_import)]
#![no_std]
extern crate std;
#[prelude_import]
use ::std::prelude::rust_2015::*;
//@ pretty-mode:expanded
//@ pp-exact:asm-operand-order.pp
//@ only-x86_64
use std::arch::asm;
pub fn main() {
unsafe {
asm!("{1}", out("rax") _, in(reg) 4);
asm!("{1}", out("rax") _, in(reg) 4, options(nostack));
asm!("{1} {2}", out("rax") _, in(reg) 4, in(reg) 5);
}
}
```
**after** (this branch):
```rust
#![feature(prelude_import)]
#![no_std]
extern crate std;
#[prelude_import]
use ::std::prelude::rust_2015::*;
//@ pretty-mode:expanded
//@ pp-exact:asm-operand-order.pp
//@ only-x86_64
use std::arch::asm;
pub fn main() {
unsafe {
asm!("{0}", in(reg) 4, out("rax") _);
asm!("{0}", in(reg) 4, out("rax") _, options(nostack));
asm!("{0} {1}", in(reg) 4, in(reg) 5, out("rax") _);
}
}
```
Notice the operand reordering: in the before, explicit register operands (`out("rax")`) appear before positional operands (`in(reg)`), violating the grammar (E0662). The template references are also wrong (`{1}` instead of `{0}`). The after moves positional operands first and renumbers the template references accordingly.
This commit is contained in:
@@ -1649,6 +1649,85 @@ fn print_mac(&mut self, m: &ast::MacCall) {
|
||||
);
|
||||
}
|
||||
|
||||
fn inline_asm_template_and_operands<'asm>(
|
||||
asm: &'asm ast::InlineAsm,
|
||||
) -> (String, Vec<&'asm InlineAsmOperand>) {
|
||||
fn is_explicit_reg(op: &InlineAsmOperand) -> bool {
|
||||
match op {
|
||||
InlineAsmOperand::In { reg, .. }
|
||||
| InlineAsmOperand::Out { reg, .. }
|
||||
| InlineAsmOperand::InOut { reg, .. }
|
||||
| InlineAsmOperand::SplitInOut { reg, .. } => {
|
||||
matches!(reg, InlineAsmRegOrRegClass::Reg(_))
|
||||
}
|
||||
InlineAsmOperand::Const { .. }
|
||||
| InlineAsmOperand::Sym { .. }
|
||||
| InlineAsmOperand::Label { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
// After macro expansion, named operands become positional. The grammar
|
||||
// requires positional operands to precede explicit register operands,
|
||||
// so we must reorder when any non-explicit operand follows an explicit
|
||||
// one. When no reordering is needed, we use the original template
|
||||
// string and operand order to avoid duplicating the Display logic in
|
||||
// InlineAsmTemplatePiece.
|
||||
let needs_reorder = {
|
||||
let mut seen_explicit = false;
|
||||
asm.operands.iter().any(|(op, _)| {
|
||||
if is_explicit_reg(op) {
|
||||
seen_explicit = true;
|
||||
false
|
||||
} else {
|
||||
seen_explicit
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
if !needs_reorder {
|
||||
let template = InlineAsmTemplatePiece::to_string(&asm.template);
|
||||
let operands = asm.operands.iter().map(|(op, _)| op).collect();
|
||||
return (template, operands);
|
||||
}
|
||||
|
||||
let mut non_explicit = Vec::new();
|
||||
let mut explicit = Vec::new();
|
||||
for (i, (op, _)) in asm.operands.iter().enumerate() {
|
||||
if is_explicit_reg(op) {
|
||||
explicit.push(i);
|
||||
} else {
|
||||
non_explicit.push(i);
|
||||
}
|
||||
}
|
||||
let order = non_explicit.into_iter().chain(explicit).collect::<Vec<_>>();
|
||||
|
||||
// Build old-index -> new-index mapping for template renumbering.
|
||||
let mut old_to_new = vec![0usize; asm.operands.len()];
|
||||
for (new_idx, old_idx) in order.iter().copied().enumerate() {
|
||||
old_to_new[old_idx] = new_idx;
|
||||
}
|
||||
|
||||
// Remap template placeholder indices and reuse the existing Display
|
||||
// impl to build the template string.
|
||||
let remapped = asm
|
||||
.template
|
||||
.iter()
|
||||
.map(|piece| match piece {
|
||||
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } => {
|
||||
InlineAsmTemplatePiece::Placeholder {
|
||||
operand_idx: old_to_new[*operand_idx],
|
||||
modifier: *modifier,
|
||||
span: *span,
|
||||
}
|
||||
}
|
||||
other => other.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let template = InlineAsmTemplatePiece::to_string(&remapped);
|
||||
let operands = order.iter().map(|&idx| &asm.operands[idx].0).collect();
|
||||
(template, operands)
|
||||
}
|
||||
|
||||
fn print_inline_asm(&mut self, asm: &ast::InlineAsm) {
|
||||
enum AsmArg<'a> {
|
||||
Template(String),
|
||||
@@ -1657,8 +1736,9 @@ enum AsmArg<'a> {
|
||||
Options(InlineAsmOptions),
|
||||
}
|
||||
|
||||
let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
|
||||
args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
|
||||
let (template, operands) = Self::inline_asm_template_and_operands(asm);
|
||||
let mut args = vec![AsmArg::Template(template)];
|
||||
args.extend(operands.into_iter().map(AsmArg::Operand));
|
||||
for (abi, _) in &asm.clobber_abis {
|
||||
args.push(AsmArg::ClobberAbi(*abi));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#![feature(prelude_import)]
|
||||
#![no_std]
|
||||
extern crate std;
|
||||
#[prelude_import]
|
||||
use ::std::prelude::rust_2015::*;
|
||||
//@ pretty-mode:expanded
|
||||
//@ pp-exact:asm-operand-order.pp
|
||||
//@ only-x86_64
|
||||
|
||||
use std::arch::asm;
|
||||
|
||||
pub fn main() {
|
||||
unsafe {
|
||||
asm!("{0}", in(reg) 4, out("rax") _);
|
||||
asm!("{0}", in(reg) 4, out("rax") _, options(nostack));
|
||||
asm!("{0} {1}", in(reg) 4, in(reg) 5, out("rax") _);
|
||||
asm!("{0}", const 5, out("rax") _);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//@ pretty-mode:expanded
|
||||
//@ pp-exact:asm-operand-order.pp
|
||||
//@ only-x86_64
|
||||
|
||||
use std::arch::asm;
|
||||
|
||||
pub fn main() {
|
||||
unsafe {
|
||||
asm!("{val}", out("rax") _, val = in(reg) 4);
|
||||
asm!("{val}", out("rax") _, val = in(reg) 4, options(nostack));
|
||||
asm!("{a} {b}", out("rax") _, a = in(reg) 4, b = in(reg) 5);
|
||||
asm!("{val}", out("rax") _, val = const 5);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user