about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2021-04-05 00:24:34 +0200
committerGitHub <noreply@github.com>2021-04-05 00:24:34 +0200
commitf8709ec96211b821c9fe8516d73d512eb718a15e (patch)
tree0517691d8a4a555d2d7a68fbed6baf1d381310fa
parent3c2e4ff525a710987c4e01805edf051740cb022b (diff)
parent31d0459207d3623cabf931ed6b272b45592c2a7e (diff)
downloadrust-f8709ec96211b821c9fe8516d73d512eb718a15e.tar.gz
rust-f8709ec96211b821c9fe8516d73d512eb718a15e.zip
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``
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs82
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs27
-rw-r--r--src/doc/unstable-book/src/library-features/asm.md8
-rw-r--r--src/test/codegen/asm-target-clobbers.rs21
4 files changed, 104 insertions, 34 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 32fb8d1c8f4..d5a9d7ee6e3 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1499,46 +1499,64 @@ 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;
                 }
-                for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
-                    if let Some(feature) = feature {
-                        if self.sess.target_features.contains(&Symbol::intern(feature)) {
+
+                // 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)) {
+                                required_features.clear();
+                                break;
+                            } else {
+                                required_features.push(feature);
+                            }
+                        } else {
                             required_features.clear();
                             break;
-                        } else {
-                            required_features.push(feature);
                         }
-                    } else {
-                        required_features.clear();
-                        break;
                     }
-                }
-                // We are sorting primitive strs here and can use unstable sort here
-                required_features.sort_unstable();
-                required_features.dedup();
-                match &required_features[..] {
-                    [] => {}
-                    [feature] => {
-                        let msg = format!(
-                            "register class `{}` requires the `{}` target feature",
-                            reg_class.name(),
-                            feature
-                        );
-                        sess.struct_span_err(op_sp, &msg).emit();
-                    }
-                    features => {
-                        let msg = format!(
-                            "register class `{}` requires at least one target feature: {}",
-                            reg_class.name(),
-                            features.join(", ")
-                        );
-                        sess.struct_span_err(op_sp, &msg).emit();
+                    // We are sorting primitive strs here and can use unstable sort here
+                    required_features.sort_unstable();
+                    required_features.dedup();
+                    match &required_features[..] {
+                        [] => {}
+                        [feature] => {
+                            let msg = format!(
+                                "register class `{}` requires the `{}` target feature",
+                                reg_class.name(),
+                                feature
+                            );
+                            sess.struct_span_err(op_sp, &msg).emit();
+                        }
+                        features => {
+                            let msg = format!(
+                                "register class `{}` requires at least one target feature: {}",
+                                reg_class.name(),
+                                features.join(", ")
+                            );
+                            sess.struct_span_err(op_sp, &msg).emit();
+                        }
                     }
                 }
 
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index e7d359c4f14..84b091d8d4d 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -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 => {
diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md
index c0e23b834d1..25d8629a38f 100644
--- a/src/doc/unstable-book/src/library-features/asm.md
+++ b/src/doc/unstable-book/src/library-features/asm.md
@@ -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") _,
         )
     }
 }
diff --git a/src/test/codegen/asm-target-clobbers.rs b/src/test/codegen/asm-target-clobbers.rs
new file mode 100644
index 00000000000..f637cdcd234
--- /dev/null
+++ b/src/test/codegen/asm-target-clobbers.rs
@@ -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));
+}