From 4d3a7ed885d6d9a81b4ec893bd08c3dc42edd22d Mon Sep 17 00:00:00 2001
From: George Wort <george.wort@arm.com>
Date: Thu, 7 Dec 2023 15:57:33 +0000
Subject: [PATCH] Add #[rustc_intrinsic_const_vector_arg] to allow vectors to
 be passed as constants

This allows constant vectors using a repr(simd) type to be propagated
through to the backend by reusing the functionality used to do a similar
thing for the simd_shuffle intrinsic.

fix #118209
---
 Cargo.lock                                    |  1 +
 compiler/rustc_borrowck/Cargo.toml            |  1 +
 compiler/rustc_borrowck/messages.ftl          |  2 +
 .../rustc_borrowck/src/session_diagnostics.rs |  8 ++
 compiler/rustc_borrowck/src/type_check/mod.rs | 33 ++++++-
 compiler/rustc_codegen_gcc/src/common.rs      |  5 +
 compiler/rustc_codegen_llvm/src/common.rs     |  8 +-
 compiler/rustc_codegen_ssa/messages.ftl       |  2 +
 compiler/rustc_codegen_ssa/src/errors.rs      |  7 ++
 compiler/rustc_codegen_ssa/src/mir/block.rs   | 55 ++++++++++-
 .../rustc_codegen_ssa/src/mir/constant.rs     | 36 ++++----
 .../rustc_codegen_ssa/src/traits/consts.rs    |  1 +
 compiler/rustc_feature/src/builtin_attrs.rs   |  3 +
 compiler/rustc_passes/messages.ftl            | 12 +++
 compiler/rustc_passes/src/check_attr.rs       | 92 ++++++++++++++++++-
 compiler/rustc_passes/src/errors.rs           | 37 ++++++++
 compiler/rustc_span/src/symbol.rs             |  1 +
 .../crates/hir-def/src/attr/builtin.rs        |  1 +
 .../rustc_intrinsic_const_vector_arg.rs       | 58 ++++++++++++
 .../rustc_intrinsic_const_vector_arg.stderr   | 68 ++++++++++++++
 .../rustc_intrinsic_const_vector_arg_calls.rs | 34 +++++++
 ...tc_intrinsic_const_vector_arg_calls.stderr | 20 ++++
 .../simd/intrinsic/inlining-issue67557-ice.rs |  4 +-
 .../ui/simd/intrinsic/inlining-issue67557.rs  |  4 +-
 24 files changed, 460 insertions(+), 33 deletions(-)
 create mode 100644 tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.rs
 create mode 100644 tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.stderr
 create mode 100644 tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.rs
 create mode 100644 tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.stderr

diff --git a/Cargo.lock b/Cargo.lock
index 98c030b3996a8..35ee0462e4ae9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3496,6 +3496,7 @@ dependencies = [
  "either",
  "itertools",
  "polonius-engine",
+ "rustc_ast",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml
index 714f46270f92c..53744626c8f7f 100644
--- a/compiler/rustc_borrowck/Cargo.toml
+++ b/compiler/rustc_borrowck/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2021"
 either = "1.5.0"
 itertools = "0.11"
 polonius-engine = "0.13.0"
+rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index 8c5a1d8970922..a659821518b55 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -71,6 +71,8 @@ borrowck_higher_ranked_lifetime_error =
 borrowck_higher_ranked_subtype_error =
     higher-ranked subtype error
 
+borrowck_intrinsic_const_vector_arg_non_const = argument at index {$index} must be a constant
+
 borrowck_lifetime_constraints_error =
     lifetime may not live long enough
 
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index e321b92603d38..bf9141d12696b 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -459,3 +459,11 @@ pub(crate) struct SimdShuffleLastConst {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(borrowck_intrinsic_const_vector_arg_non_const)]
+pub(crate) struct IntrinsicConstVectorArgNonConst {
+    #[primary_span]
+    pub span: Span,
+    pub index: u128,
+}
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 98c21693cf0a0..9583ba9d6ec0d 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -49,7 +49,9 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::ResultsCursor;
 
-use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst};
+use crate::session_diagnostics::{
+    IntrinsicConstVectorArgNonConst, MoveUnsized, SimdShuffleLastConst,
+};
 use crate::{
     borrow_set::BorrowSet,
     constraints::{OutlivesConstraint, OutlivesConstraintSet},
@@ -1579,6 +1581,35 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
                     _ => {}
                 }
+            } else if let Some(attr) =
+                self.tcx().get_attr(def_id, sym::rustc_intrinsic_const_vector_arg)
+            {
+                match attr.meta_item_list() {
+                    Some(items) => {
+                        items.into_iter().for_each(|item: rustc_ast::NestedMetaItem| match item {
+                            rustc_ast::NestedMetaItem::Lit(rustc_ast::MetaItemLit {
+                                kind: rustc_ast::LitKind::Int(index, _),
+                                ..
+                            }) => {
+                                if index >= args.len() as u128 {
+                                    span_mirbug!(self, term, "index out of bounds");
+                                } else {
+                                    if !matches!(args[index as usize], Operand::Constant(_)) {
+                                        self.tcx().sess.emit_err(IntrinsicConstVectorArgNonConst {
+                                            span: term.source_info.span,
+                                            index,
+                                        });
+                                    }
+                                }
+                            }
+                            _ => {
+                                span_mirbug!(self, term, "invalid index");
+                            }
+                        });
+                    }
+                    // Error is reported by `rustc_attr!`
+                    None => (),
+                }
             }
         }
         debug!(?func_ty);
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index b7ddc410315c3..5d80bd1399d0b 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -159,6 +159,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         self.context.new_struct_constructor(None, struct_type.as_type(), None, values)
     }
 
+    fn const_vector(&self, values: &[RValue<'gcc>]) -> RValue<'gcc> {
+        let typ = self.type_vector(values[0].get_type(), values.len() as u64);
+        self.context.new_rvalue_from_vector(None, typ, values)
+    }
+
     fn const_to_opt_uint(&self, _v: RValue<'gcc>) -> Option<u64> {
         // TODO(antoyo)
         None
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 8173e41aff457..6f92e4116f985 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -98,10 +98,6 @@ impl<'ll> CodegenCx<'ll, '_> {
         unsafe { llvm::LLVMConstArray(ty, elts.as_ptr(), elts.len() as c_uint) }
     }
 
-    pub fn const_vector(&self, elts: &[&'ll Value]) -> &'ll Value {
-        unsafe { llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint) }
-    }
-
     pub fn const_bytes(&self, bytes: &[u8]) -> &'ll Value {
         bytes_in_context(self.llcx, bytes)
     }
@@ -217,6 +213,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         struct_in_context(self.llcx, elts, packed)
     }
 
+    fn const_vector(&self, elts: &[&'ll Value]) -> &'ll Value {
+        unsafe { llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint) }
+    }
+
     fn const_to_opt_uint(&self, v: &'ll Value) -> Option<u64> {
         try_as_const_integral(v).and_then(|v| unsafe {
             let mut i = 0u64;
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 5881c6236ece6..1144282483f3c 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -16,6 +16,8 @@ codegen_ssa_cgu_not_recorded =
 
 codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option.
 
+codegen_ssa_const_vector_evaluation = could not evaluate constant vector at compile time
+
 codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error}
 
 codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error}
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index ed6ac9f9c5da8..e7e2a9d31bc4a 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -663,6 +663,13 @@ pub struct ShuffleIndicesEvaluation {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_const_vector_evaluation)]
+pub struct ConstVectorEvaluation {
+    #[primary_span]
+    pub span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(codegen_ssa_missing_memory_ordering)]
 pub struct MissingMemoryOrdering;
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index a1662f25e1496..1a92a3ac4ac9f 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -5,12 +5,13 @@ use super::{CachedLlbb, FunctionCx, LocalRef};
 
 use crate::base;
 use crate::common::{self, IntPredicate};
+use crate::errors;
 use crate::meth;
 use crate::traits::*;
 use crate::MemFlags;
 
 use rustc_ast as ast;
-use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece, LitKind, MetaItemLit, NestedMetaItem};
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir::{self, AssertKind, SwitchTargets, UnwindTerminateReason};
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
@@ -864,7 +865,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         // promotes any complex rvalues to constants.
                         if i == 2 && intrinsic == sym::simd_shuffle {
                             if let mir::Operand::Constant(constant) = arg {
-                                let (llval, ty) = self.simd_shuffle_indices(bx, constant);
+                                let (llval, ty) = self.early_evaluate_const_vector(bx, constant);
+                                let llval = llval.unwrap_or_else(|| {
+                                    bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation {
+                                        span: constant.span,
+                                    });
+                                    // We've errored, so we don't have to produce working code.
+                                    let llty = bx.backend_type(bx.layout_of(ty));
+                                    bx.const_undef(llty)
+                                });
                                 return OperandRef {
                                     val: Immediate(llval),
                                     layout: bx.layout_of(ty),
@@ -908,9 +917,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             (args, None)
         };
 
+        let const_vec_arg_indexes = (|| {
+            if let Some(def) = def
+                && let Some(attr) =
+                    bx.tcx().get_attr(def.def_id(), sym::rustc_intrinsic_const_vector_arg)
+            {
+                attr.meta_item_list()
+                    .iter()
+                    .flatten()
+                    .map(|item: &NestedMetaItem| match item {
+                        NestedMetaItem::Lit(MetaItemLit {
+                            kind: LitKind::Int(index, _), ..
+                        }) => *index as usize,
+                        _ => span_bug!(item.span(), "attribute argument must be an integer"),
+                    })
+                    .collect()
+            } else {
+                Vec::<usize>::new()
+            }
+        })();
+
         let mut copied_constant_arguments = vec![];
         'make_args: for (i, arg) in first_args.iter().enumerate() {
-            let mut op = self.codegen_operand(bx, arg);
+            let mut op = if const_vec_arg_indexes.contains(&i) {
+                // Force the specified argument to be constant by using const-qualification to promote any complex rvalues to constant.
+                if let mir::Operand::Constant(constant) = arg
+                    && constant.ty().is_simd()
+                {
+                    let (llval, ty) = self.early_evaluate_const_vector(bx, &constant);
+                    let llval = llval.unwrap_or_else(|| {
+                        bx.tcx()
+                            .sess
+                            .emit_err(errors::ConstVectorEvaluation { span: constant.span });
+                        // We've errored, so we don't have to produce working code.
+                        let llty = bx.backend_type(bx.layout_of(ty));
+                        bx.const_undef(llty)
+                    });
+                    OperandRef { val: Immediate(llval), layout: bx.layout_of(ty) }
+                } else {
+                    span_bug!(span, "argument at {i} must be a constant vector");
+                }
+            } else {
+                self.codegen_operand(bx, arg)
+            };
 
             if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
                 match op.val {
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index 558f64fffc263..4ad97147ba7c1 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -1,4 +1,3 @@
-use crate::errors;
 use crate::mir::operand::OperandRef;
 use crate::traits::*;
 use rustc_middle::mir;
@@ -28,7 +27,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             .expect("erroneous constant not captured by required_consts")
     }
 
-    /// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
+    /// This is a convenience helper for `early_evaluate_const_vector`. It has the precondition
     /// that the given `constant` is an `Const::Unevaluated` and must be convertible to
     /// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip.
     ///
@@ -63,19 +62,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         )
     }
 
-    /// process constant containing SIMD shuffle indices
-    pub fn simd_shuffle_indices(
+    /// process constant SIMD vector or constant containing SIMD shuffle indices
+    pub fn early_evaluate_const_vector(
         &mut self,
         bx: &Bx,
         constant: &mir::ConstOperand<'tcx>,
-    ) -> (Bx::Value, Ty<'tcx>) {
+    ) -> (Option<Bx::Value>, Ty<'tcx>) {
         let ty = self.monomorphize(constant.ty());
-        let val = self
-            .eval_unevaluated_mir_constant_to_valtree(constant)
-            .ok()
-            .flatten()
-            .map(|val| {
-                let field_ty = ty.builtin_index().unwrap();
+        let field_ty = if ty.is_simd() {
+            ty.simd_size_and_type(bx.tcx()).1
+        } else {
+            ty.builtin_index().unwrap()
+        };
+        let val =
+            self.eval_unevaluated_mir_constant_to_valtree(constant).ok().flatten().map(|val| {
                 let values: Vec<_> = val
                     .unwrap_branch()
                     .iter()
@@ -87,17 +87,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             };
                             bx.scalar_to_backend(prim, scalar, bx.immediate_backend_type(layout))
                         } else {
-                            bug!("simd shuffle field {:?}", field)
+                            bug!("field is not a scalar {:?}", field)
                         }
                     })
                     .collect();
-                bx.const_struct(&values, false)
-            })
-            .unwrap_or_else(|| {
-                bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span: constant.span });
-                // We've errored, so we don't have to produce working code.
-                let llty = bx.backend_type(bx.layout_of(ty));
-                bx.const_undef(llty)
+                if ty.is_simd() {
+                    bx.const_vector(&values)
+                } else {
+                    bx.const_struct(&values, false)
+                }
             });
         (val, ty)
     }
diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs
index 4dff9c7684f18..4c2cdfb37ded7 100644
--- a/compiler/rustc_codegen_ssa/src/traits/consts.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs
@@ -28,6 +28,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
 
     fn const_str(&self, s: &str) -> (Self::Value, Self::Value);
     fn const_struct(&self, elts: &[Self::Value], packed: bool) -> Self::Value;
+    fn const_vector(&self, elts: &[Self::Value]) -> Self::Value;
 
     fn const_to_opt_uint(&self, v: Self::Value) -> Option<u64>;
     fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option<u128>;
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 5523543cd4fb9..ae42c3fd319be 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -652,6 +652,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_const_panic_str, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
     ),
+    rustc_attr!(
+        rustc_intrinsic_const_vector_arg, Normal, template!(List: "arg1_index, arg2_index, ..."), ErrorFollowing, INTERNAL_UNSTABLE
+    ),
 
     // ==========================================================================
     // Internal attributes, Layout related:
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index be50aad13032f..39cd5707937ab 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -604,6 +604,18 @@ passes_rustc_allow_const_fn_unstable =
 passes_rustc_dirty_clean =
     attribute requires -Z query-dep-graph to be enabled
 
+passes_rustc_intrinsic_const_vector_arg =
+    attribute should be applied to functions in `extern "unadjusted"` modules
+    .label = not a function in an `extern "unadjusted"` module
+
+passes_rustc_intrinsic_const_vector_arg_invalid = attribute requires a parameter index
+
+passes_rustc_intrinsic_const_vector_arg_non_vector = parameter at index {$index} must be a simd type
+    .label = parameter is a non-simd type
+
+passes_rustc_intrinsic_const_vector_arg_out_of_bounds = function does not have a parameter at index {$index}
+    .label = function has {$arg_count} arguments
+
 passes_rustc_layout_scalar_valid_range_arg =
     expected exactly one integer literal argument
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c5073048be3e8..b1ab56787baec 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -10,10 +10,12 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, IntoDiagnosticArg, MultiSpan};
 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
-use rustc_hir::def_id::LocalModDefId;
+use rustc_hir::def::Res;
+use rustc_hir::def_id::{DefId, LocalModDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{
-    self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
+    self, FnSig, ForeignItem, HirId, Item, ItemKind, Path, PathSegment, QPath, TraitItem, TyKind,
+    CRATE_HIR_ID, CRATE_OWNER_ID,
 };
 use rustc_hir::{MethodKind, Target, Unsafety};
 use rustc_macros::LintDiagnostic;
@@ -198,6 +200,9 @@ impl CheckAttrVisitor<'_> {
                 sym::rustc_safe_intrinsic => {
                     self.check_rustc_safe_intrinsic(hir_id, attr, span, target)
                 }
+                sym::rustc_intrinsic_const_vector_arg => {
+                    self.check_rustc_intrinsic_const_vector_arg(hir_id, attr, span, target)
+                }
                 _ => true,
             };
 
@@ -2083,6 +2088,89 @@ impl CheckAttrVisitor<'_> {
         false
     }
 
+    fn check_rustc_intrinsic_const_vector_arg(
+        &self,
+        hir_id: HirId,
+        attr: &Attribute,
+        span: Span,
+        target: Target,
+    ) -> bool {
+        let hir = self.tcx.hir();
+
+        if let Target::ForeignFn = target
+            && let Some(parent) = hir.opt_parent_id(hir_id)
+            && let hir::Node::Item(Item {
+                kind: ItemKind::ForeignMod { abi: Abi::Unadjusted, .. },
+                ..
+            }) = hir.get(parent)
+        {
+            let Some(list) = attr.meta_item_list() else {
+                // The attribute form is validated on AST.
+                return false;
+            };
+
+            let Some(decl) = self.tcx.hir().get(hir_id).fn_decl() else {
+                bug!("should be a function declaration");
+            };
+
+            let arg_count = decl.inputs.len() as u128;
+            for meta in list {
+                if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) {
+                    if *val >= arg_count {
+                        self.tcx.sess.emit_err(errors::RustcIntrinsicConstVectorArgOutOfBounds {
+                            attr_span: attr.span,
+                            span: span,
+                            index: *val,
+                            arg_count: decl.inputs.len(),
+                        });
+                        return false;
+                    }
+
+                    let param_ty = decl.inputs[*val as usize];
+                    let type_id: Option<DefId> = match param_ty.kind {
+                        TyKind::Path(path) => match path {
+                            QPath::Resolved(_, Path { res: Res::Def(_, id), .. }) => Some(*id),
+                            QPath::TypeRelative(_, PathSegment { res: Res::Def(_, id), .. }) => {
+                                Some(*id)
+                            }
+                            _ => None,
+                        },
+                        _ => None,
+                    };
+
+                    let is_simd = if let Some(type_id) = type_id {
+                        self.tcx
+                            .get_attrs(type_id, sym::repr)
+                            .filter_map(|attr| attr.meta_item_list())
+                            .flatten()
+                            .any(|hint: NestedMetaItem| hint.name_or_empty() == sym::simd)
+                    } else {
+                        false
+                    };
+                    if !is_simd {
+                        self.tcx.sess.emit_err(errors::RustcIntrinsicConstVectorArgNonVector {
+                            attr_span: attr.span,
+                            param_span: param_ty.span,
+                            index: *val,
+                        });
+                        return false;
+                    }
+                } else {
+                    self.tcx.sess.emit_err(errors::RustcIntrinsicConstVectorArgInvalid {
+                        span: meta.span(),
+                    });
+                    return false;
+                }
+            }
+        } else {
+            self.tcx
+                .sess
+                .emit_err(errors::RustcIntrinsicConstVectorArg { attr_span: attr.span, span });
+            return false;
+        }
+        true
+    }
+
     fn check_rustc_std_internal_symbol(
         &self,
         attr: &Attribute,
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 5812744532280..0b15ff35f8137 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -636,6 +636,43 @@ pub struct RustcSafeIntrinsic {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(passes_rustc_intrinsic_const_vector_arg_out_of_bounds)]
+pub(crate) struct RustcIntrinsicConstVectorArgOutOfBounds {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+    pub index: u128,
+    pub arg_count: usize,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_rustc_intrinsic_const_vector_arg_non_vector)]
+pub(crate) struct RustcIntrinsicConstVectorArgNonVector {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub param_span: Span,
+    pub index: u128,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_rustc_intrinsic_const_vector_arg_invalid)]
+pub(crate) struct RustcIntrinsicConstVectorArgInvalid {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_rustc_intrinsic_const_vector_arg)]
+pub struct RustcIntrinsicConstVectorArg {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(passes_rustc_std_internal_symbol)]
 pub struct RustcStdInternalSymbol {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 0333b5f04c3bc..ea7525b587363 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1403,6 +1403,7 @@ symbols! {
         rustc_if_this_changed,
         rustc_inherit_overflow_checks,
         rustc_insignificant_dtor,
+        rustc_intrinsic_const_vector_arg,
         rustc_layout,
         rustc_layout_scalar_valid_range_end,
         rustc_layout_scalar_valid_range_start,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index 15dceeb8af254..f1ce043b9c571 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -488,6 +488,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
     ),
+    rustc_attr!(rustc_intrinsic_const_vector_arg, Normal, template!(List), ErrorFollowing, INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Layout related:
diff --git a/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.rs b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.rs
new file mode 100644
index 0000000000000..2d4bbd470372d
--- /dev/null
+++ b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.rs
@@ -0,0 +1,58 @@
+// compile-flags: -Z unstable-options
+#![feature(abi_unadjusted)]
+#![feature(inline_const)]
+#![feature(intrinsics)]
+#![allow(non_camel_case_types)]
+#![feature(repr_simd)]
+#![feature(rustc_attrs)]
+#![feature(simd_ffi)]
+#![allow(unused)]
+
+#[repr(simd)]
+#[derive(Clone)]
+pub struct i8x2(i8, i8);
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg] //~ ERROR malformed `rustc_intrinsic_const_vector_arg` attribute input
+    fn foo1(a: i8x2, b: i8);
+}
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg = "1"] //~ ERROR malformed `rustc_intrinsic_const_vector_arg` attribute input
+    fn foo2(a: i8x2, b: i8);
+}
+
+#[rustc_intrinsic_const_vector_arg(0)] //~ ERROR  attribute should be applied to functions in `extern "unadjusted"` modules
+pub struct foo3(i8x2);
+
+extern "C" {
+    #[rustc_intrinsic_const_vector_arg(0)] //~ ERROR  attribute should be applied to functions in `extern "unadjusted"` modules
+    fn foo4(a: i8x2);
+}
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg(0)] //~ ERROR function does not have a parameter at index 0
+    fn foo5();
+}
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg(1)] //~ ERROR function does not have a parameter at index 1
+    fn foo6(a: i8x2);
+}
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg("bar")] //~ ERROR attribute requires a parameter index
+    fn foo7(a: i8x2);
+}
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg(0,2)] //~ ERROR function does not have a parameter at index 2
+    fn foo8(a: i8x2, b: i8);
+}
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg(0)] //~ ERROR parameter at index 0 must be a simd type
+    fn foo9(a: i8);
+}
+
+fn main() {}
diff --git a/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.stderr b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.stderr
new file mode 100644
index 0000000000000..3182a9151d8c9
--- /dev/null
+++ b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg.stderr
@@ -0,0 +1,68 @@
+error: malformed `rustc_intrinsic_const_vector_arg` attribute input
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:16:5
+   |
+LL |     #[rustc_intrinsic_const_vector_arg]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_intrinsic_const_vector_arg(arg1_index, arg2_index, ...)]`
+
+error: malformed `rustc_intrinsic_const_vector_arg` attribute input
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:21:5
+   |
+LL |     #[rustc_intrinsic_const_vector_arg = "1"]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_intrinsic_const_vector_arg(arg1_index, arg2_index, ...)]`
+
+error: attribute should be applied to functions in `extern "unadjusted"` modules
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:25:1
+   |
+LL | #[rustc_intrinsic_const_vector_arg(0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | pub struct foo3(i8x2);
+   | ---------------------- not a function in an `extern "unadjusted"` module
+
+error: attribute should be applied to functions in `extern "unadjusted"` modules
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:29:5
+   |
+LL |     #[rustc_intrinsic_const_vector_arg(0)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo4(a: i8x2);
+   |     ----------------- not a function in an `extern "unadjusted"` module
+
+error: function does not have a parameter at index 0
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:34:5
+   |
+LL |     #[rustc_intrinsic_const_vector_arg(0)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo5();
+   |     ---------- function has 0 arguments
+
+error: function does not have a parameter at index 1
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:39:5
+   |
+LL |     #[rustc_intrinsic_const_vector_arg(1)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo6(a: i8x2);
+   |     ----------------- function has 1 arguments
+
+error: attribute requires a parameter index
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:44:40
+   |
+LL |     #[rustc_intrinsic_const_vector_arg("bar")]
+   |                                        ^^^^^
+
+error: function does not have a parameter at index 2
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:49:5
+   |
+LL |     #[rustc_intrinsic_const_vector_arg(0,2)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo8(a: i8x2, b: i8);
+   |     ------------------------ function has 2 arguments
+
+error: parameter at index 0 must be a simd type
+  --> $DIR/rustc_intrinsic_const_vector_arg.rs:54:5
+   |
+LL |     #[rustc_intrinsic_const_vector_arg(0)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo9(a: i8);
+   |                -- parameter is a non-simd type
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.rs b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.rs
new file mode 100644
index 0000000000000..00c341ff4f423
--- /dev/null
+++ b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.rs
@@ -0,0 +1,34 @@
+// compile-flags: -Z unstable-options
+#![feature(abi_unadjusted)]
+#![feature(inline_const)]
+#![feature(intrinsics)]
+#![allow(non_camel_case_types)]
+#![feature(repr_simd)]
+#![feature(rustc_attrs)]
+#![feature(simd_ffi)]
+#![allow(unused)]
+
+#[repr(simd)]
+#[derive(Clone)]
+pub struct i8x2(i8, i8);
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg(0)] // OK
+    fn foo1(a: i8x2);
+}
+
+extern "unadjusted" {
+    #[rustc_intrinsic_const_vector_arg(0,1)] // OK
+    fn foo2(a: i8x2, b: i8x2);
+}
+
+fn main() {
+    unsafe {
+        foo1(i8x2(0,1)); //~ ERROR argument at index 0 must be a constant
+        foo1({ i8x2(0,1) }); //~ ERROR argument at index 0 must be a constant
+        foo1(const { i8x2(0,1) }); // OK
+
+        foo2(const { i8x2(0,1) }, { i8x2(2,3) }); //~ ERROR argument at index 1 must be a constant
+        foo2(const { i8x2(0,1) }, const { i8x2(2,3) }); // OK
+    }
+}
diff --git a/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.stderr b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.stderr
new file mode 100644
index 0000000000000..43a7d73efa5c3
--- /dev/null
+++ b/tests/ui/internal-lints/rustc_intrinsic_const_vector_arg_calls.stderr
@@ -0,0 +1,20 @@
+error: argument at index 0 must be a constant
+  --> $DIR/rustc_intrinsic_const_vector_arg_calls.rs:27:9
+   |
+LL |         foo1(i8x2(0,1));
+   |         ^^^^^^^^^^^^^^^
+
+error: argument at index 0 must be a constant
+  --> $DIR/rustc_intrinsic_const_vector_arg_calls.rs:28:9
+   |
+LL |         foo1({ i8x2(0,1) });
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: argument at index 1 must be a constant
+  --> $DIR/rustc_intrinsic_const_vector_arg_calls.rs:31:9
+   |
+LL |         foo2(const { i8x2(0,1) }, { i8x2(2,3) });
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs b/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs
index 5ca684a9d7831..6dc9ff250c4ab 100644
--- a/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs
+++ b/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs
@@ -1,5 +1,5 @@
-// This used to cause an ICE for an internal index out of range due to simd_shuffle_indices being
-// passed the wrong Instance, causing issues with inlining. See #67557.
+// This used to cause an ICE for an internal index out of range due to early_evaluate_const_vector
+// being passed the wrong Instance, causing issues with inlining. See #67557.
 //
 // run-pass
 // compile-flags: -Zmir-opt-level=4
diff --git a/tests/ui/simd/intrinsic/inlining-issue67557.rs b/tests/ui/simd/intrinsic/inlining-issue67557.rs
index 5633ad70cd318..f2b634d4bea4e 100644
--- a/tests/ui/simd/intrinsic/inlining-issue67557.rs
+++ b/tests/ui/simd/intrinsic/inlining-issue67557.rs
@@ -1,5 +1,5 @@
-// This used to cause assert_10_13 to unexpectingly fail, due to simd_shuffle_indices being passed
-// the wrong Instance, causing issues with inlining. See #67557.
+// This used to cause assert_10_13 to unexpectingly fail, due to early_evaluate_const_vector
+// being passed the wrong Instance, causing issues with inlining. See #67557.
 //
 // run-pass
 // compile-flags: -Zmir-opt-level=4