Skip to content

Commit f24ce9b

Browse files
committed
Auto merge of #82838 - Amanieu:rustdoc_asm, r=nagisa
Allow rustdoc to handle asm! of foreign architectures This allows rustdoc to process code containing `asm!` for architectures other than the current one. Since this never reaches codegen, we just replace target-specific registers and register classes with a dummy one. Fixes #82869
2 parents 195ad48 + ba00ddc commit f24ce9b

File tree

7 files changed

+139
-45
lines changed

7 files changed

+139
-45
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+44-45
Original file line numberDiff line numberDiff line change
@@ -1331,84 +1331,83 @@ impl<'hir> LoweringContext<'_, 'hir> {
13311331
}
13321332

13331333
fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> {
1334-
if self.sess.asm_arch.is_none() {
1334+
// Rustdoc needs to support asm! from foriegn architectures: don't try
1335+
// lowering the register contraints in this case.
1336+
let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch };
1337+
if asm_arch.is_none() && !self.sess.opts.actually_rustdoc {
13351338
struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit();
13361339
}
13371340
if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
1338-
&& !matches!(
1339-
self.sess.asm_arch,
1340-
Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64)
1341-
)
1341+
&& !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
1342+
&& !self.sess.opts.actually_rustdoc
13421343
{
13431344
self.sess
13441345
.struct_span_err(sp, "the `att_syntax` option is only supported on x86")
13451346
.emit();
13461347
}
13471348

1348-
// Lower operands to HIR, filter_map skips any operands with invalid
1349-
// register classes.
1349+
// Lower operands to HIR. We use dummy register classes if an error
1350+
// occurs during lowering because we still need to be able to produce a
1351+
// valid HIR.
13501352
let sess = self.sess;
13511353
let operands: Vec<_> = asm
13521354
.operands
13531355
.iter()
1354-
.filter_map(|(op, op_sp)| {
1355-
let lower_reg = |reg| {
1356-
Some(match reg {
1357-
InlineAsmRegOrRegClass::Reg(s) => asm::InlineAsmRegOrRegClass::Reg(
1356+
.map(|(op, op_sp)| {
1357+
let lower_reg = |reg| match reg {
1358+
InlineAsmRegOrRegClass::Reg(s) => {
1359+
asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
13581360
asm::InlineAsmReg::parse(
1359-
sess.asm_arch?,
1361+
asm_arch,
13601362
|feature| sess.target_features.contains(&Symbol::intern(feature)),
13611363
&sess.target,
13621364
s,
13631365
)
1364-
.map_err(|e| {
1366+
.unwrap_or_else(|e| {
13651367
let msg = format!("invalid register `{}`: {}", s.as_str(), e);
13661368
sess.struct_span_err(*op_sp, &msg).emit();
1369+
asm::InlineAsmReg::Err
13671370
})
1368-
.ok()?,
1369-
),
1370-
InlineAsmRegOrRegClass::RegClass(s) => {
1371-
asm::InlineAsmRegOrRegClass::RegClass(
1372-
asm::InlineAsmRegClass::parse(sess.asm_arch?, s)
1373-
.map_err(|e| {
1374-
let msg = format!(
1375-
"invalid register class `{}`: {}",
1376-
s.as_str(),
1377-
e
1378-
);
1379-
sess.struct_span_err(*op_sp, &msg).emit();
1380-
})
1381-
.ok()?,
1382-
)
1383-
}
1384-
})
1371+
} else {
1372+
asm::InlineAsmReg::Err
1373+
})
1374+
}
1375+
InlineAsmRegOrRegClass::RegClass(s) => {
1376+
asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
1377+
asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
1378+
let msg = format!("invalid register class `{}`: {}", s.as_str(), e);
1379+
sess.struct_span_err(*op_sp, &msg).emit();
1380+
asm::InlineAsmRegClass::Err
1381+
})
1382+
} else {
1383+
asm::InlineAsmRegClass::Err
1384+
})
1385+
}
13851386
};
13861387

1387-
// lower_reg is executed last because we need to lower all
1388-
// sub-expressions even if we throw them away later.
13891388
let op = match *op {
13901389
InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
1390+
reg: lower_reg(reg),
13911391
expr: self.lower_expr_mut(expr),
1392-
reg: lower_reg(reg)?,
13931392
},
13941393
InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
1394+
reg: lower_reg(reg),
13951395
late,
13961396
expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
1397-
reg: lower_reg(reg)?,
13981397
},
13991398
InlineAsmOperand::InOut { reg, late, ref expr } => {
14001399
hir::InlineAsmOperand::InOut {
1400+
reg: lower_reg(reg),
14011401
late,
14021402
expr: self.lower_expr_mut(expr),
1403-
reg: lower_reg(reg)?,
14041403
}
14051404
}
14061405
InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
14071406
hir::InlineAsmOperand::SplitInOut {
1407+
reg: lower_reg(reg),
14081408
late,
14091409
in_expr: self.lower_expr_mut(in_expr),
14101410
out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
1411-
reg: lower_reg(reg)?,
14121411
}
14131412
}
14141413
InlineAsmOperand::Const { ref expr } => {
@@ -1418,17 +1417,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
14181417
hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) }
14191418
}
14201419
};
1421-
Some((op, *op_sp))
1420+
(op, *op_sp)
14221421
})
14231422
.collect();
14241423

1425-
// Stop if there were any errors when lowering the register classes
1426-
if operands.len() != asm.operands.len() || sess.asm_arch.is_none() {
1427-
return hir::ExprKind::Err;
1428-
}
1429-
14301424
// Validate template modifiers against the register classes for the operands
1431-
let asm_arch = sess.asm_arch.unwrap();
14321425
for p in &asm.template {
14331426
if let InlineAsmTemplatePiece::Placeholder {
14341427
operand_idx,
@@ -1443,7 +1436,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
14431436
| hir::InlineAsmOperand::InOut { reg, .. }
14441437
| hir::InlineAsmOperand::SplitInOut { reg, .. } => {
14451438
let class = reg.reg_class();
1446-
let valid_modifiers = class.valid_modifiers(asm_arch);
1439+
if class == asm::InlineAsmRegClass::Err {
1440+
continue;
1441+
}
1442+
let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
14471443
if !valid_modifiers.contains(&modifier) {
14481444
let mut err = sess.struct_span_err(
14491445
placeholder_span,
@@ -1506,7 +1502,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
15061502
// features. We check that at least one type is available for
15071503
// the current target.
15081504
let reg_class = reg.reg_class();
1509-
for &(_, feature) in reg_class.supported_types(asm_arch) {
1505+
if reg_class == asm::InlineAsmRegClass::Err {
1506+
continue;
1507+
}
1508+
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
15101509
if let Some(feature) = feature {
15111510
if self.sess.target_features.contains(&Symbol::intern(feature)) {
15121511
required_features.clear();

compiler/rustc_codegen_llvm/src/asm.rs

+3
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
528528
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
529529
bug!("LLVM backend does not support SPIR-V")
530530
}
531+
InlineAsmRegClass::Err => unreachable!(),
531532
}
532533
.to_string(),
533534
}
@@ -594,6 +595,7 @@ fn modifier_to_llvm(
594595
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
595596
bug!("LLVM backend does not support SPIR-V")
596597
}
598+
InlineAsmRegClass::Err => unreachable!(),
597599
}
598600
}
599601

@@ -637,6 +639,7 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
637639
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
638640
bug!("LLVM backend does not support SPIR-V")
639641
}
642+
InlineAsmRegClass::Err => unreachable!(),
640643
}
641644
}
642645

compiler/rustc_target/src/asm/mod.rs

+14
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ pub enum InlineAsmReg {
229229
Mips(MipsInlineAsmReg),
230230
SpirV(SpirVInlineAsmReg),
231231
Wasm(WasmInlineAsmReg),
232+
// Placeholder for invalid register constraints for the current target
233+
Err,
232234
}
233235

234236
impl InlineAsmReg {
@@ -240,6 +242,7 @@ impl InlineAsmReg {
240242
Self::RiscV(r) => r.name(),
241243
Self::Hexagon(r) => r.name(),
242244
Self::Mips(r) => r.name(),
245+
Self::Err => "<reg>",
243246
}
244247
}
245248

@@ -251,6 +254,7 @@ impl InlineAsmReg {
251254
Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()),
252255
Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()),
253256
Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()),
257+
Self::Err => InlineAsmRegClass::Err,
254258
}
255259
}
256260

@@ -309,6 +313,7 @@ impl InlineAsmReg {
309313
Self::RiscV(r) => r.emit(out, arch, modifier),
310314
Self::Hexagon(r) => r.emit(out, arch, modifier),
311315
Self::Mips(r) => r.emit(out, arch, modifier),
316+
Self::Err => unreachable!("Use of InlineAsmReg::Err"),
312317
}
313318
}
314319

@@ -320,6 +325,7 @@ impl InlineAsmReg {
320325
Self::RiscV(_) => cb(self),
321326
Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))),
322327
Self::Mips(_) => cb(self),
328+
Self::Err => unreachable!("Use of InlineAsmReg::Err"),
323329
}
324330
}
325331
}
@@ -346,6 +352,8 @@ pub enum InlineAsmRegClass {
346352
Mips(MipsInlineAsmRegClass),
347353
SpirV(SpirVInlineAsmRegClass),
348354
Wasm(WasmInlineAsmRegClass),
355+
// Placeholder for invalid register constraints for the current target
356+
Err,
349357
}
350358

351359
impl InlineAsmRegClass {
@@ -360,6 +368,7 @@ impl InlineAsmRegClass {
360368
Self::Mips(r) => r.name(),
361369
Self::SpirV(r) => r.name(),
362370
Self::Wasm(r) => r.name(),
371+
Self::Err => rustc_span::symbol::sym::reg,
363372
}
364373
}
365374

@@ -377,6 +386,7 @@ impl InlineAsmRegClass {
377386
Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips),
378387
Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV),
379388
Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm),
389+
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
380390
}
381391
}
382392

@@ -401,6 +411,7 @@ impl InlineAsmRegClass {
401411
Self::Mips(r) => r.suggest_modifier(arch, ty),
402412
Self::SpirV(r) => r.suggest_modifier(arch, ty),
403413
Self::Wasm(r) => r.suggest_modifier(arch, ty),
414+
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
404415
}
405416
}
406417

@@ -421,6 +432,7 @@ impl InlineAsmRegClass {
421432
Self::Mips(r) => r.default_modifier(arch),
422433
Self::SpirV(r) => r.default_modifier(arch),
423434
Self::Wasm(r) => r.default_modifier(arch),
435+
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
424436
}
425437
}
426438

@@ -440,6 +452,7 @@ impl InlineAsmRegClass {
440452
Self::Mips(r) => r.supported_types(arch),
441453
Self::SpirV(r) => r.supported_types(arch),
442454
Self::Wasm(r) => r.supported_types(arch),
455+
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
443456
}
444457
}
445458

@@ -476,6 +489,7 @@ impl InlineAsmRegClass {
476489
Self::Mips(r) => r.valid_modifiers(arch),
477490
Self::SpirV(r) => r.valid_modifiers(arch),
478491
Self::Wasm(r) => r.valid_modifiers(arch),
492+
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
479493
}
480494
}
481495
}

src/test/rustdoc/asm-foreign.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Make sure rustdoc accepts asm! for a foreign architecture.
2+
3+
#![feature(asm)]
4+
5+
// @has asm_foreign/fn.aarch64.html
6+
pub unsafe fn aarch64(a: f64, b: f64) -> f64 {
7+
let c;
8+
asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") {
9+
|| {};
10+
b
11+
});
12+
c
13+
}
14+
15+
// @has asm_foreign/fn.x86.html
16+
pub unsafe fn x86(a: f64, b: f64) -> f64 {
17+
let c;
18+
asm!("addsd {}, {}, xmm0", out(xmm_reg) c, in(xmm_reg) a, in("xmm0") b);
19+
c
20+
}

src/test/rustdoc/asm-foreign2.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// only-aarch64
2+
// Make sure rustdoc accepts options(att_syntax) asm! on non-x86 targets.
3+
4+
#![feature(asm)]
5+
6+
// @has asm_foreign2/fn.x86.html
7+
pub unsafe fn x86(x: i64) -> i64 {
8+
let y;
9+
asm!("movq {}, {}", in(reg) x, out(reg) y, options(att_syntax));
10+
y
11+
}

src/test/ui/issues/issue-82869.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// only-x86_64
2+
// Make sure rustc doesn't ICE on asm! for a foreign architecture.
3+
4+
#![feature(asm)]
5+
#![crate_type = "rlib"]
6+
7+
pub unsafe fn aarch64(a: f64, b: f64) -> f64 {
8+
let c;
9+
asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") {
10+
|| {};
11+
b
12+
});
13+
//~^^^^ invalid register class
14+
//~^^^^^ invalid register class
15+
//~^^^^^^ invalid register
16+
c
17+
}
18+
19+
pub unsafe fn x86(a: f64, b: f64) -> f64 {
20+
let c;
21+
asm!("addsd {}, {}, xmm0", out(xmm_reg) c, in(xmm_reg) a, in("xmm0") b);
22+
c
23+
}

src/test/ui/issues/issue-82869.stderr

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error: invalid register class `vreg`: unknown register class
2+
--> $DIR/issue-82869.rs:9:32
3+
|
4+
LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") {
5+
| ^^^^^^^^^^^
6+
7+
error: invalid register class `vreg`: unknown register class
8+
--> $DIR/issue-82869.rs:9:45
9+
|
10+
LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") {
11+
| ^^^^^^^^^^
12+
13+
error: invalid register `d0`: unknown register
14+
--> $DIR/issue-82869.rs:9:57
15+
|
16+
LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") {
17+
| _________________________________________________________^
18+
LL | | || {};
19+
LL | | b
20+
LL | | });
21+
| |_____^
22+
23+
error: aborting due to 3 previous errors
24+

0 commit comments

Comments
 (0)