mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-05 11:33:04 +00:00
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
@ -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 {
|
||||
|
@ -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 => {
|
||||
|
@ -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") _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
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…
Reference in New Issue
Block a user