Skip to content

Commit 78ae63b

Browse files
authored
Rollup merge of #83841 - Amanieu:asm_clobber_feature, r=nagisa
Allow clobbering unsupported registers in asm! Previously registers could only be marked as clobbered if the target feature for that register was enabled. This restriction is now removed. cc #81092 r? `@nagisa`
2 parents ade2667 + 31d0459 commit 78ae63b

File tree

4 files changed

+104
-34
lines changed

4 files changed

+104
-34
lines changed

Diff for: compiler/rustc_ast_lowering/src/expr.rs

+50-32
Original file line numberDiff line numberDiff line change
@@ -1499,46 +1499,64 @@ impl<'hir> LoweringContext<'_, 'hir> {
14991499
// previous iteration.
15001500
required_features.clear();
15011501

1502-
// Validate register classes against currently enabled target
1503-
// features. We check that at least one type is available for
1504-
// the current target.
15051502
let reg_class = reg.reg_class();
15061503
if reg_class == asm::InlineAsmRegClass::Err {
15071504
continue;
15081505
}
1509-
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
1510-
if let Some(feature) = feature {
1511-
if self.sess.target_features.contains(&Symbol::intern(feature)) {
1506+
1507+
// We ignore target feature requirements for clobbers: if the
1508+
// feature is disabled then the compiler doesn't care what we
1509+
// do with the registers.
1510+
//
1511+
// Note that this is only possible for explicit register
1512+
// operands, which cannot be used in the asm string.
1513+
let is_clobber = matches!(
1514+
op,
1515+
hir::InlineAsmOperand::Out {
1516+
reg: asm::InlineAsmRegOrRegClass::Reg(_),
1517+
late: _,
1518+
expr: None
1519+
}
1520+
);
1521+
1522+
if !is_clobber {
1523+
// Validate register classes against currently enabled target
1524+
// features. We check that at least one type is available for
1525+
// the current target.
1526+
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
1527+
if let Some(feature) = feature {
1528+
if self.sess.target_features.contains(&Symbol::intern(feature)) {
1529+
required_features.clear();
1530+
break;
1531+
} else {
1532+
required_features.push(feature);
1533+
}
1534+
} else {
15121535
required_features.clear();
15131536
break;
1514-
} else {
1515-
required_features.push(feature);
15161537
}
1517-
} else {
1518-
required_features.clear();
1519-
break;
15201538
}
1521-
}
1522-
// We are sorting primitive strs here and can use unstable sort here
1523-
required_features.sort_unstable();
1524-
required_features.dedup();
1525-
match &required_features[..] {
1526-
[] => {}
1527-
[feature] => {
1528-
let msg = format!(
1529-
"register class `{}` requires the `{}` target feature",
1530-
reg_class.name(),
1531-
feature
1532-
);
1533-
sess.struct_span_err(op_sp, &msg).emit();
1534-
}
1535-
features => {
1536-
let msg = format!(
1537-
"register class `{}` requires at least one target feature: {}",
1538-
reg_class.name(),
1539-
features.join(", ")
1540-
);
1541-
sess.struct_span_err(op_sp, &msg).emit();
1539+
// We are sorting primitive strs here and can use unstable sort here
1540+
required_features.sort_unstable();
1541+
required_features.dedup();
1542+
match &required_features[..] {
1543+
[] => {}
1544+
[feature] => {
1545+
let msg = format!(
1546+
"register class `{}` requires the `{}` target feature",
1547+
reg_class.name(),
1548+
feature
1549+
);
1550+
sess.struct_span_err(op_sp, &msg).emit();
1551+
}
1552+
features => {
1553+
let msg = format!(
1554+
"register class `{}` requires at least one target feature: {}",
1555+
reg_class.name(),
1556+
features.join(", ")
1557+
);
1558+
sess.struct_span_err(op_sp, &msg).emit();
1559+
}
15421560
}
15431561
}
15441562

Diff for: compiler/rustc_codegen_llvm/src/asm.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashMap;
1414
use rustc_hir as hir;
1515
use rustc_middle::ty::layout::TyAndLayout;
1616
use rustc_middle::{bug, span_bug};
17-
use rustc_span::{Pos, Span};
17+
use rustc_span::{Pos, Span, Symbol};
1818
use rustc_target::abi::*;
1919
use rustc_target::asm::*;
2020

@@ -125,15 +125,39 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
125125

126126
// Collect the types of output operands
127127
let mut constraints = vec![];
128+
let mut clobbers = vec![];
128129
let mut output_types = vec![];
129130
let mut op_idx = FxHashMap::default();
130131
for (idx, op) in operands.iter().enumerate() {
131132
match *op {
132133
InlineAsmOperandRef::Out { reg, late, place } => {
134+
let is_target_supported = |reg_class: InlineAsmRegClass| {
135+
for &(_, feature) in reg_class.supported_types(asm_arch) {
136+
if let Some(feature) = feature {
137+
if self.tcx.sess.target_features.contains(&Symbol::intern(feature))
138+
{
139+
return true;
140+
}
141+
} else {
142+
// Register class is unconditionally supported
143+
return true;
144+
}
145+
}
146+
false
147+
};
148+
133149
let mut layout = None;
134150
let ty = if let Some(ref place) = place {
135151
layout = Some(&place.layout);
136152
llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout)
153+
} else if !is_target_supported(reg.reg_class()) {
154+
// We turn discarded outputs into clobber constraints
155+
// if the target feature needed by the register class is
156+
// disabled. This is necessary otherwise LLVM will try
157+
// to actually allocate a register for the dummy output.
158+
assert!(matches!(reg, InlineAsmRegOrRegClass::Reg(_)));
159+
clobbers.push(format!("~{}", reg_to_llvm(reg, None)));
160+
continue;
137161
} else {
138162
// If the output is discarded, we don't really care what
139163
// type is used. We're just using this to tell LLVM to
@@ -244,6 +268,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
244268
}
245269
}
246270

271+
constraints.append(&mut clobbers);
247272
if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
248273
match asm_arch {
249274
InlineAsmArch::AArch64 | InlineAsmArch::Arm => {

Diff for: src/doc/unstable-book/src/library-features/asm.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,19 @@ fn call_foo(arg: i32) {
306306
sym foo,
307307
// 1st argument in rdi, which is caller-saved
308308
inout("rdi") arg => _,
309-
// All caller-saved registers must be marked as clobberred
309+
// All caller-saved registers must be marked as clobbered
310310
out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _,
311311
out("r8") _, out("r9") _, out("r10") _, out("r11") _,
312312
out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
313313
out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
314314
out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
315315
out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
316+
// Also mark AVX-512 registers as clobbered. This is accepted by the
317+
// compiler even if AVX-512 is not enabled on the current target.
318+
out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _,
319+
out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm13") _,
320+
out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _,
321+
out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _,
316322
)
317323
}
318324
}

Diff for: src/test/codegen/asm-target-clobbers.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// only-x86_64
2+
// revisions: base avx512
3+
// [avx512]compile-flags: -C target-feature=+avx512f
4+
5+
#![crate_type = "rlib"]
6+
#![feature(asm)]
7+
8+
// CHECK-LABEL: @avx512_clobber
9+
// base: call void asm sideeffect inteldialect "", "~{xmm31}"()
10+
// avx512: call float asm sideeffect inteldialect "", "=&{xmm31}"()
11+
#[no_mangle]
12+
pub unsafe fn avx512_clobber() {
13+
asm!("", out("zmm31") _, options(nostack, nomem, preserves_flags));
14+
}
15+
16+
// CHECK-LABEL: @eax_clobber
17+
// CHECK: call i32 asm sideeffect inteldialect "", "=&{ax}"()
18+
#[no_mangle]
19+
pub unsafe fn eax_clobber() {
20+
asm!("", out("eax") _, options(nostack, nomem, preserves_flags));
21+
}

0 commit comments

Comments
 (0)