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``
This commit is contained in:
commit
f8709ec962
4 changed files with 104 additions and 34 deletions
|
@ -1499,13 +1499,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
// previous iteration.
|
// previous iteration.
|
||||||
required_features.clear();
|
required_features.clear();
|
||||||
|
|
||||||
// Validate register classes against currently enabled target
|
|
||||||
// features. We check that at least one type is available for
|
|
||||||
// the current target.
|
|
||||||
let reg_class = reg.reg_class();
|
let reg_class = reg.reg_class();
|
||||||
if reg_class == asm::InlineAsmRegClass::Err {
|
if reg_class == asm::InlineAsmRegClass::Err {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We ignore target feature requirements for clobbers: if the
|
||||||
|
// feature is disabled then the compiler doesn't care what we
|
||||||
|
// do with the registers.
|
||||||
|
//
|
||||||
|
// Note that this is only possible for explicit register
|
||||||
|
// operands, which cannot be used in the asm string.
|
||||||
|
let is_clobber = matches!(
|
||||||
|
op,
|
||||||
|
hir::InlineAsmOperand::Out {
|
||||||
|
reg: asm::InlineAsmRegOrRegClass::Reg(_),
|
||||||
|
late: _,
|
||||||
|
expr: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if !is_clobber {
|
||||||
|
// Validate register classes against currently enabled target
|
||||||
|
// features. We check that at least one type is available for
|
||||||
|
// the current target.
|
||||||
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
|
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
|
||||||
if let Some(feature) = feature {
|
if let Some(feature) = feature {
|
||||||
if self.sess.target_features.contains(&Symbol::intern(feature)) {
|
if self.sess.target_features.contains(&Symbol::intern(feature)) {
|
||||||
|
@ -1541,6 +1558,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
sess.struct_span_err(op_sp, &msg).emit();
|
sess.struct_span_err(op_sp, &msg).emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for conflicts between explicit register operands.
|
// Check for conflicts between explicit register operands.
|
||||||
if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
|
if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
|
||||||
|
|
|
@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_middle::ty::layout::TyAndLayout;
|
use rustc_middle::ty::layout::TyAndLayout;
|
||||||
use rustc_middle::{bug, span_bug};
|
use rustc_middle::{bug, span_bug};
|
||||||
use rustc_span::{Pos, Span};
|
use rustc_span::{Pos, Span, Symbol};
|
||||||
use rustc_target::abi::*;
|
use rustc_target::abi::*;
|
||||||
use rustc_target::asm::*;
|
use rustc_target::asm::*;
|
||||||
|
|
||||||
|
@ -125,15 +125,39 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
|
|
||||||
// Collect the types of output operands
|
// Collect the types of output operands
|
||||||
let mut constraints = vec![];
|
let mut constraints = vec![];
|
||||||
|
let mut clobbers = vec![];
|
||||||
let mut output_types = vec![];
|
let mut output_types = vec![];
|
||||||
let mut op_idx = FxHashMap::default();
|
let mut op_idx = FxHashMap::default();
|
||||||
for (idx, op) in operands.iter().enumerate() {
|
for (idx, op) in operands.iter().enumerate() {
|
||||||
match *op {
|
match *op {
|
||||||
InlineAsmOperandRef::Out { reg, late, place } => {
|
InlineAsmOperandRef::Out { reg, late, place } => {
|
||||||
|
let is_target_supported = |reg_class: InlineAsmRegClass| {
|
||||||
|
for &(_, feature) in reg_class.supported_types(asm_arch) {
|
||||||
|
if let Some(feature) = feature {
|
||||||
|
if self.tcx.sess.target_features.contains(&Symbol::intern(feature))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Register class is unconditionally supported
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
let mut layout = None;
|
let mut layout = None;
|
||||||
let ty = if let Some(ref place) = place {
|
let ty = if let Some(ref place) = place {
|
||||||
layout = Some(&place.layout);
|
layout = Some(&place.layout);
|
||||||
llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout)
|
llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout)
|
||||||
|
} else if !is_target_supported(reg.reg_class()) {
|
||||||
|
// We turn discarded outputs into clobber constraints
|
||||||
|
// if the target feature needed by the register class is
|
||||||
|
// disabled. This is necessary otherwise LLVM will try
|
||||||
|
// to actually allocate a register for the dummy output.
|
||||||
|
assert!(matches!(reg, InlineAsmRegOrRegClass::Reg(_)));
|
||||||
|
clobbers.push(format!("~{}", reg_to_llvm(reg, None)));
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// If the output is discarded, we don't really care what
|
// If the output is discarded, we don't really care what
|
||||||
// type is used. We're just using this to tell LLVM to
|
// type is used. We're just using this to tell LLVM to
|
||||||
|
@ -244,6 +268,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraints.append(&mut clobbers);
|
||||||
if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
|
if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
|
||||||
match asm_arch {
|
match asm_arch {
|
||||||
InlineAsmArch::AArch64 | InlineAsmArch::Arm => {
|
InlineAsmArch::AArch64 | InlineAsmArch::Arm => {
|
||||||
|
|
|
@ -306,13 +306,19 @@ fn call_foo(arg: i32) {
|
||||||
sym foo,
|
sym foo,
|
||||||
// 1st argument in rdi, which is caller-saved
|
// 1st argument in rdi, which is caller-saved
|
||||||
inout("rdi") arg => _,
|
inout("rdi") arg => _,
|
||||||
// All caller-saved registers must be marked as clobberred
|
// All caller-saved registers must be marked as clobbered
|
||||||
out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _,
|
out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _,
|
||||||
out("r8") _, out("r9") _, out("r10") _, out("r11") _,
|
out("r8") _, out("r9") _, out("r10") _, out("r11") _,
|
||||||
out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
|
out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
|
||||||
out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
|
out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
|
||||||
out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
|
out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
|
||||||
out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
|
out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
|
||||||
|
// Also mark AVX-512 registers as clobbered. This is accepted by the
|
||||||
|
// compiler even if AVX-512 is not enabled on the current target.
|
||||||
|
out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _,
|
||||||
|
out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm13") _,
|
||||||
|
out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _,
|
||||||
|
out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
src/test/codegen/asm-target-clobbers.rs
Normal file
21
src/test/codegen/asm-target-clobbers.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// only-x86_64
|
||||||
|
// revisions: base avx512
|
||||||
|
// [avx512]compile-flags: -C target-feature=+avx512f
|
||||||
|
|
||||||
|
#![crate_type = "rlib"]
|
||||||
|
#![feature(asm)]
|
||||||
|
|
||||||
|
// CHECK-LABEL: @avx512_clobber
|
||||||
|
// base: call void asm sideeffect inteldialect "", "~{xmm31}"()
|
||||||
|
// avx512: call float asm sideeffect inteldialect "", "=&{xmm31}"()
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn avx512_clobber() {
|
||||||
|
asm!("", out("zmm31") _, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @eax_clobber
|
||||||
|
// CHECK: call i32 asm sideeffect inteldialect "", "=&{ax}"()
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn eax_clobber() {
|
||||||
|
asm!("", out("eax") _, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue