about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl4
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs76
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs5
-rw-r--r--compiler/rustc_codegen_cranelift/src/inline_asm.rs8
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs52
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl3
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs38
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs8
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--compiler/rustc_target/src/asm/mod.rs13
-rw-r--r--compiler/rustc_target/src/asm/s390x.rs13
14 files changed, 186 insertions, 59 deletions
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index f704320c71d..a4dbf981115 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -152,6 +152,8 @@ ast_lowering_register2 = register `{$reg2_name}`
 
 ast_lowering_register_class_only_clobber =
     register class `{$reg_class_name}` can only be used as a clobber, not as an input or output
+ast_lowering_register_class_only_clobber_stable =
+    register class `{$reg_class_name}` can only be used as a clobber in stable
 
 ast_lowering_register_conflict =
     register `{$reg1_name}` conflicts with register `{$reg2_name}`
@@ -181,6 +183,8 @@ ast_lowering_underscore_expr_lhs_assign =
     .label = `_` not allowed here
 
 ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture
+ast_lowering_unstable_inline_assembly_label_operand_with_outputs =
+    using both label and output operands for inline assembly is unstable
 ast_lowering_unstable_inline_assembly_label_operands =
     label operands for inline assembly are unstable
 ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 215e6d84d0f..ff803e50997 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -17,7 +17,8 @@ use super::errors::{
     InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst,
     InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass,
     InvalidAsmTemplateModifierRegClassSub, InvalidAsmTemplateModifierSym, InvalidRegister,
-    InvalidRegisterClass, RegisterClassOnlyClobber, RegisterConflict,
+    InvalidRegisterClass, RegisterClassOnlyClobber, RegisterClassOnlyClobberStable,
+    RegisterConflict,
 };
 use crate::{
     AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode,
@@ -61,6 +62,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 .emit();
             }
         }
+        let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
         if asm.options.contains(InlineAsmOptions::ATT_SYNTAX)
             && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
             && !self.tcx.sess.opts.actually_rustdoc
@@ -239,15 +241,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         }
                     }
                     InlineAsmOperand::Label { block } => {
-                        if !self.tcx.features().asm_goto() {
-                            feature_err(
-                                sess,
-                                sym::asm_goto,
-                                *op_sp,
-                                fluent::ast_lowering_unstable_inline_assembly_label_operands,
-                            )
-                            .emit();
-                        }
                         hir::InlineAsmOperand::Label { block: self.lower_block(block, false) }
                     }
                 };
@@ -333,11 +326,29 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 // means that we disallow passing a value in/out of the asm and
                 // require that the operand name an explicit register, not a
                 // register class.
-                if reg_class.is_clobber_only(asm_arch.unwrap()) && !op.is_clobber() {
-                    self.dcx().emit_err(RegisterClassOnlyClobber {
-                        op_span: op_sp,
-                        reg_class_name: reg_class.name(),
-                    });
+                if reg_class.is_clobber_only(asm_arch.unwrap(), allow_experimental_reg)
+                    && !op.is_clobber()
+                {
+                    if allow_experimental_reg || reg_class.is_clobber_only(asm_arch.unwrap(), true)
+                    {
+                        // always clobber-only
+                        self.dcx().emit_err(RegisterClassOnlyClobber {
+                            op_span: op_sp,
+                            reg_class_name: reg_class.name(),
+                        });
+                    } else {
+                        // clobber-only in stable
+                        self.tcx
+                            .sess
+                            .create_feature_err(
+                                RegisterClassOnlyClobberStable {
+                                    op_span: op_sp,
+                                    reg_class_name: reg_class.name(),
+                                },
+                                sym::asm_experimental_reg,
+                            )
+                            .emit();
+                    }
                     continue;
                 }
 
@@ -466,6 +477,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
         }
 
+        // Feature gate checking for asm goto.
+        if let Some((_, op_sp)) =
+            operands.iter().find(|(op, _)| matches!(op, hir::InlineAsmOperand::Label { .. }))
+        {
+            if !self.tcx.features().asm_goto() {
+                feature_err(
+                    sess,
+                    sym::asm_goto,
+                    *op_sp,
+                    fluent::ast_lowering_unstable_inline_assembly_label_operands,
+                )
+                .emit();
+            }
+
+            // In addition, check if an output operand is used.
+            // This is gated behind an additional feature.
+            let output_operand_used = operands.iter().any(|(op, _)| {
+                matches!(
+                    op,
+                    hir::InlineAsmOperand::Out { expr: Some(_), .. }
+                        | hir::InlineAsmOperand::InOut { .. }
+                        | hir::InlineAsmOperand::SplitInOut { out_expr: Some(_), .. }
+                )
+            });
+            if output_operand_used && !self.tcx.features().asm_goto_with_outputs() {
+                feature_err(
+                    sess,
+                    sym::asm_goto_with_outputs,
+                    *op_sp,
+                    fluent::ast_lowering_unstable_inline_assembly_label_operand_with_outputs,
+                )
+                .emit();
+            }
+        }
+
         let operands = self.arena.alloc_from_iter(operands);
         let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
         let template_strs = self.arena.alloc_from_iter(
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index e6a3f939f2d..447af57354f 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -280,6 +280,14 @@ pub(crate) struct RegisterClassOnlyClobber {
 }
 
 #[derive(Diagnostic)]
+#[diag(ast_lowering_register_class_only_clobber_stable)]
+pub(crate) struct RegisterClassOnlyClobberStable {
+    #[primary_span]
+    pub op_span: Span,
+    pub reg_class_name: Symbol,
+}
+
+#[derive(Diagnostic)]
 #[diag(ast_lowering_register_conflict)]
 pub(crate) struct RegisterConflict<'a> {
     #[primary_span]
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 9ae48024f44..14ac3cd74e8 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -300,7 +300,10 @@ pub fn parse_asm_args<'a>(
     if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
         dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
     }
-    if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
+    if args.options.contains(ast::InlineAsmOptions::NORETURN)
+        && !outputs_sp.is_empty()
+        && labels_sp.is_empty()
+    {
         let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
         // Bail out now since this is likely to confuse MIR
         return Err(err);
diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
index 70176754f33..7bc500b1814 100644
--- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
@@ -462,8 +462,12 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
         let mut slots_output = vec![None; self.operands.len()];
 
         let new_slot_fn = |slot_size: &mut Size, reg_class: InlineAsmRegClass| {
-            let reg_size =
-                reg_class.supported_types(self.arch).iter().map(|(ty, _)| ty.size()).max().unwrap();
+            let reg_size = reg_class
+                .supported_types(self.arch, true)
+                .iter()
+                .map(|(ty, _)| ty.size())
+                .max()
+                .unwrap();
             let align = rustc_abi::Align::from_bytes(reg_size.bytes()).unwrap();
             let offset = slot_size.align_to(align);
             *slot_size = offset + reg_size;
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index 6b067b35e71..ab4fdb78bb0 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -186,7 +186,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
                             // `clobber_abi` can add lots of clobbers that are not supported by the target,
                             // such as AVX-512 registers, so we just ignore unsupported registers
                             let is_target_supported =
-                                reg.reg_class().supported_types(asm_arch).iter().any(
+                                reg.reg_class().supported_types(asm_arch, true).iter().any(
                                     |&(_, feature)| {
                                         if let Some(feature) = feature {
                                             self.tcx
@@ -683,9 +683,8 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
-            InlineAsmRegClass::S390x(
-                S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg,
-            ) => {
+            InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg) => "v",
+            InlineAsmRegClass::S390x(S390xInlineAsmRegClass::areg) => {
                 unreachable!("clobber-only")
             }
             InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => "r",
@@ -766,7 +765,8 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
             S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr,
         ) => cx.type_i32(),
         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
-        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
+        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i64(), 2),
+        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::areg) => {
             unreachable!("clobber-only")
         }
         InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => cx.type_i32(),
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index bb74dfe1487..6ee80c08d4a 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -45,7 +45,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
             match *op {
                 InlineAsmOperandRef::Out { reg, late, place } => {
                     let is_target_supported = |reg_class: InlineAsmRegClass| {
-                        for &(_, feature) in reg_class.supported_types(asm_arch) {
+                        for &(_, feature) in reg_class.supported_types(asm_arch, true) {
                             if let Some(feature) = feature {
                                 if self
                                     .tcx
@@ -85,7 +85,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                         }
                         continue;
                     } else if !is_target_supported(reg.reg_class())
-                        || reg.reg_class().is_clobber_only(asm_arch)
+                        || reg.reg_class().is_clobber_only(asm_arch, true)
                     {
                         // We turn discarded outputs into clobber constraints
                         // if the target feature needed by the register class is
@@ -342,24 +342,32 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
         }
         attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });
 
-        // Switch to the 'normal' basic block if we did an `invoke` instead of a `call`
-        if let Some(dest) = dest {
-            self.switch_to_block(dest);
-        }
+        // Write results to outputs. We need to do this for all possible control flow.
+        //
+        // Note that `dest` maybe populated with unreachable_block when asm goto with outputs
+        // is used (because we need to codegen callbr which always needs a destination), so
+        // here we use the NORETURN option to determine if `dest` should be used.
+        for block in (if options.contains(InlineAsmOptions::NORETURN) { None } else { Some(dest) })
+            .into_iter()
+            .chain(labels.iter().copied().map(Some))
+        {
+            if let Some(block) = block {
+                self.switch_to_block(block);
+            }
 
-        // Write results to outputs
-        for (idx, op) in operands.iter().enumerate() {
-            if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }
-            | InlineAsmOperandRef::InOut { reg, out_place: Some(place), .. } = *op
-            {
-                let value = if output_types.len() == 1 {
-                    result
-                } else {
-                    self.extract_value(result, op_idx[&idx] as u64)
-                };
-                let value =
-                    llvm_fixup_output(self, value, reg.reg_class(), &place.layout, instance);
-                OperandValue::Immediate(value).store(self, place);
+            for (idx, op) in operands.iter().enumerate() {
+                if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }
+                | InlineAsmOperandRef::InOut { reg, out_place: Some(place), .. } = *op
+                {
+                    let value = if output_types.len() == 1 {
+                        result
+                    } else {
+                        self.extract_value(result, op_idx[&idx] as u64)
+                    };
+                    let value =
+                        llvm_fixup_output(self, value, reg.reg_class(), &place.layout, instance);
+                    OperandValue::Immediate(value).store(self, place);
+                }
             }
         }
     }
@@ -678,7 +686,8 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
             S390x(S390xInlineAsmRegClass::reg) => "r",
             S390x(S390xInlineAsmRegClass::reg_addr) => "a",
             S390x(S390xInlineAsmRegClass::freg) => "f",
-            S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
+            S390x(S390xInlineAsmRegClass::vreg) => "v",
+            S390x(S390xInlineAsmRegClass::areg) => {
                 unreachable!("clobber-only")
             }
             Sparc(SparcInlineAsmRegClass::reg) => "r",
@@ -844,7 +853,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
         Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(),
         S390x(S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr) => cx.type_i32(),
         S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
-        S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
+        S390x(S390xInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i64(), 2),
+        S390x(S390xInlineAsmRegClass::areg) => {
             unreachable!("clobber-only")
         }
         Sparc(SparcInlineAsmRegClass::reg) => cx.type_i32(),
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index cf0f2a7e48c..aa2e451ef39 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -376,8 +376,12 @@ declare_features! (
     (unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
     /// Enables experimental inline assembly support for additional architectures.
     (unstable, asm_experimental_arch, "1.58.0", Some(93335)),
+    /// Enables experimental register support in inline assembly.
+    (unstable, asm_experimental_reg, "CURRENT_RUSTC_VERSION", Some(133416)),
     /// Allows using `label` operands in inline assembly.
     (unstable, asm_goto, "1.78.0", Some(119364)),
+    /// Allows using `label` operands in inline assembly together with output operands.
+    (unstable, asm_goto_with_outputs, "CURRENT_RUSTC_VERSION", Some(119364)),
     /// Allows the `may_unwind` option in inline assembly.
     (unstable, asm_unwind, "1.58.0", Some(93334)),
     /// Allows users to enforce equality of associated constants `TraitImpl<AssocConst=3>`.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index f5ccf8c9dff..49b4a1fabec 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -431,6 +431,9 @@ hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is
 hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}`
     .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}`
 
+hir_analysis_register_type_unstable =
+    type `{$ty}` cannot be used with this register class in stable
+
 hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}`
 
 hir_analysis_return_type_notation_equality_bound =
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index dfddf93a5c2..b96469f503c 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -7,12 +7,14 @@ use rustc_hir::{self as hir, LangItem};
 use rustc_middle::bug;
 use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
 use rustc_session::lint;
-use rustc_span::Symbol;
 use rustc_span::def_id::LocalDefId;
+use rustc_span::{Symbol, sym};
 use rustc_target::asm::{
     InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo,
 };
 
+use crate::errors::RegisterTypeUnstable;
+
 pub struct InlineAsmCtxt<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     typing_env: ty::TypingEnv<'tcx>,
@@ -218,17 +220,29 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         // Check the type against the list of types supported by the selected
         // register class.
         let asm_arch = self.tcx.sess.asm_arch.unwrap();
+        let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
         let reg_class = reg.reg_class();
-        let supported_tys = reg_class.supported_types(asm_arch);
+        let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg);
         let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
-            let msg = format!("type `{ty}` cannot be used with this register class");
-            let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
-            let supported_tys: Vec<_> = supported_tys.iter().map(|(t, _)| t.to_string()).collect();
-            err.note(format!(
-                "register class `{}` supports these types: {}",
-                reg_class.name(),
-                supported_tys.join(", "),
-            ));
+            let mut err = if !allow_experimental_reg
+                && reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty)
+            {
+                self.tcx.sess.create_feature_err(
+                    RegisterTypeUnstable { span: expr.span, ty },
+                    sym::asm_experimental_reg,
+                )
+            } else {
+                let msg = format!("type `{ty}` cannot be used with this register class");
+                let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
+                let supported_tys: Vec<_> =
+                    supported_tys.iter().map(|(t, _)| t.to_string()).collect();
+                err.note(format!(
+                    "register class `{}` supports these types: {}",
+                    reg_class.name(),
+                    supported_tys.join(", "),
+                ));
+                err
+            };
             if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
                 err.help(format!("consider using the `{}` register class instead", suggest.name()));
             }
@@ -313,6 +327,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             self.tcx.dcx().delayed_bug("target architecture does not support asm");
             return;
         };
+        let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
         for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
             // Validate register classes against currently enabled target
             // features. We check that at least one type is available for
@@ -352,7 +367,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                     if let InlineAsmRegClass::Err = reg_class {
                         continue;
                     }
-                    for &(_, feature) in reg_class.supported_types(asm_arch) {
+                    for &(_, feature) in reg_class.supported_types(asm_arch, allow_experimental_reg)
+                    {
                         match feature {
                             Some(feature) => {
                                 if target_features.contains(&feature) {
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index f5ca3c49475..0b2e9ed6052 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1708,3 +1708,11 @@ pub(crate) struct CmseEntryGeneric {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_register_type_unstable)]
+pub(crate) struct RegisterTypeUnstable<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'a>,
+}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 3d0ec2afa2b..46e245fb71f 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -416,7 +416,9 @@ symbols! {
         asm,
         asm_const,
         asm_experimental_arch,
+        asm_experimental_reg,
         asm_goto,
+        asm_goto_with_outputs,
         asm_sym,
         asm_unwind,
         assert,
@@ -2139,6 +2141,7 @@ symbols! {
         vec_pop,
         vec_with_capacity,
         vecdeque_iter,
+        vector,
         version,
         vfp2,
         vis,
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index 10778e9acf1..db8d23776e5 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -604,9 +604,13 @@ impl InlineAsmRegClass {
 
     /// Returns a list of supported types for this register class, each with an
     /// options target feature required to use this type.
+    ///
+    /// At the codegen stage, it is fine to always pass true for `allow_experimental_reg`,
+    /// since all the stability checking will have been done in prior stages.
     pub fn supported_types(
         self,
         arch: InlineAsmArch,
+        allow_experimental_reg: bool,
     ) -> &'static [(InlineAsmType, Option<Symbol>)] {
         match self {
             Self::X86(r) => r.supported_types(arch),
@@ -618,7 +622,7 @@ impl InlineAsmRegClass {
             Self::Hexagon(r) => r.supported_types(arch),
             Self::LoongArch(r) => r.supported_types(arch),
             Self::Mips(r) => r.supported_types(arch),
-            Self::S390x(r) => r.supported_types(arch),
+            Self::S390x(r) => r.supported_types(arch, allow_experimental_reg),
             Self::Sparc(r) => r.supported_types(arch),
             Self::SpirV(r) => r.supported_types(arch),
             Self::Wasm(r) => r.supported_types(arch),
@@ -696,8 +700,11 @@ impl InlineAsmRegClass {
 
     /// Returns whether registers in this class can only be used as clobbers
     /// and not as inputs/outputs.
-    pub fn is_clobber_only(self, arch: InlineAsmArch) -> bool {
-        self.supported_types(arch).is_empty()
+    ///
+    /// At the codegen stage, it is fine to always pass true for `allow_experimental_reg`,
+    /// since all the stability checking will have been done in prior stages.
+    pub fn is_clobber_only(self, arch: InlineAsmArch, allow_experimental_reg: bool) -> bool {
+        self.supported_types(arch, allow_experimental_reg).is_empty()
     }
 }
 
diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs
index 9b31190a72b..410590b722b 100644
--- a/compiler/rustc_target/src/asm/s390x.rs
+++ b/compiler/rustc_target/src/asm/s390x.rs
@@ -38,11 +38,22 @@ impl S390xInlineAsmRegClass {
     pub fn supported_types(
         self,
         _arch: InlineAsmArch,
+        allow_experimental_reg: bool,
     ) -> &'static [(InlineAsmType, Option<Symbol>)] {
         match self {
             Self::reg | Self::reg_addr => types! { _: I8, I16, I32, I64; },
             Self::freg => types! { _: F32, F64; },
-            Self::vreg => &[],
+            Self::vreg => {
+                if allow_experimental_reg {
+                    // non-clobber-only vector register support is unstable.
+                    types! {
+                        vector: I32, F32, I64, F64, I128, F128,
+                            VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
+                    }
+                } else {
+                    &[]
+                }
+            }
             Self::areg => &[],
         }
     }