Skip to content

Commit ddc53f8

Browse files
committed
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.
1 parent cbd6ec7 commit ddc53f8

File tree

3 files changed

+97
-33
lines changed

3 files changed

+97
-33
lines changed

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

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 => {
+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)