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:
Dylan DPC 2021-04-05 00:24:34 +02:00 committed by GitHub
commit f8709ec962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 34 deletions

View File

@ -1499,13 +1499,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
// previous iteration.
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();
if reg_class == asm::InlineAsmRegClass::Err {
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()) {
if let Some(feature) = 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();
}
}
}
// Check for conflicts between explicit register operands.
if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {

View File

@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::{bug, span_bug};
use rustc_span::{Pos, Span};
use rustc_span::{Pos, Span, Symbol};
use rustc_target::abi::*;
use rustc_target::asm::*;
@ -125,15 +125,39 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
// Collect the types of output operands
let mut constraints = vec![];
let mut clobbers = vec![];
let mut output_types = vec![];
let mut op_idx = FxHashMap::default();
for (idx, op) in operands.iter().enumerate() {
match *op {
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 ty = if let Some(ref place) = place {
layout = Some(&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 {
// If the output is discarded, we don't really care what
// 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) {
match asm_arch {
InlineAsmArch::AArch64 | InlineAsmArch::Arm => {

View File

@ -306,13 +306,19 @@ fn call_foo(arg: i32) {
sym foo,
// 1st argument in rdi, which is caller-saved
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("r8") _, out("r9") _, out("r10") _, out("r11") _,
out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
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") _,
)
}
}

View 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));
}