This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[SVE ACLE] Add support for svasrd


These functions require the shift amount to be an integer constant
expression in the range [1, N].  The patch adds a new hook so that
targets can check this.

Also, the ACLE asm tests try many combinations per file, so I'd added
code to parallelise them at single-file granularity.  However,
the code to do that was using variables that only exist when -j
is used and so this patch makes it conditional.

Tested on aarch64-linux-gnu and committed to aarch64/sve-acle-branch.

Richard

------------------------------------------------------------------------

diff --git a/gcc/testsuite/g++.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp b/gcc/testsuite/g++.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp
index e4a65b809a4..88cf53396e5 100644
--- a/gcc/testsuite/g++.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp
+++ b/gcc/testsuite/g++.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp
@@ -48,8 +48,10 @@ if { ![string equal $sve_flags ""] || [aarch64_sve_bits] == 0 } {
 }
 
 global gcc_runtest_parallelize_limit_minor
-set old_limit_minor $gcc_runtest_parallelize_limit_minor
-set gcc_runtest_parallelize_limit_minor 1
+if { [info exists gcc_runtest_parallelize_limit_minor] } {
+    set old_limit_minor $gcc_runtest_parallelize_limit_minor
+    set gcc_runtest_parallelize_limit_minor 1
+}
 
 torture-init
 set-torture-options {
@@ -76,7 +78,9 @@ gcc-dg-runtest [lsort $files] \
 
 torture-finish
 
-set gcc_runtest_parallelize_limit_minor $old_limit_minor
+if { [info exists gcc_runtest_parallelize_limit_minor] } {
+    set gcc_runtest_parallelize_limit_minor $old_limit_minor
+}
 
 # All done.
 dg-finish
diff --git a/gcc/testsuite/gcc.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp b/gcc/testsuite/gcc.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp
index 7dbdee2825e..b4fd451adea 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp
+++ b/gcc/testsuite/gcc.target/aarch64/sve-acle/aarch64-sve-acle-asm.exp
@@ -48,8 +48,10 @@ if { ![string equal $sve_flags ""] || [aarch64_sve_bits] == 0 } {
 }
 
 global gcc_runtest_parallelize_limit_minor
-set old_limit_minor $gcc_runtest_parallelize_limit_minor
-set gcc_runtest_parallelize_limit_minor 1
+if { [info exists gcc_runtest_parallelize_limit_minor] } {
+    set old_limit_minor $gcc_runtest_parallelize_limit_minor
+    set gcc_runtest_parallelize_limit_minor 1
+}
 
 torture-init
 set-torture-options {
@@ -72,7 +74,9 @@ gcc-dg-runtest [lsort $files] \
 
 torture-finish
 
-set gcc_runtest_parallelize_limit_minor $old_limit_minor
+if { [info exists gcc_runtest_parallelize_limit_minor] } {
+    set gcc_runtest_parallelize_limit_minor $old_limit_minor
+}
 
 # All done.
 dg-finish

------------------------------------------------------------------------

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index f5e1111a772..9bb3feedf03 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5749,16 +5749,24 @@ builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs,
 /* Verifies the NARGS arguments ARGS to the builtin function FNDECL.
    Returns false if there was an error, otherwise true.  LOC is the
    location of the function; ARG_LOC is a vector of locations of the
-   arguments.  */
+   arguments.  If FNDECL is the result of resolving an overloaded
+   target built-in, ORIG_FNDECL is the original function decl,
+   otherwise it is null.  */
 
 bool
 check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
-				  tree fndecl, int nargs, tree *args)
+				  tree fndecl, tree orig_fndecl,
+				  int nargs, tree *args)
 {
-  if (!DECL_BUILT_IN (fndecl)
-      || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
+  if (!DECL_BUILT_IN (fndecl))
     return true;
 
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+    return (!targetm.check_builtin_call
+	    || targetm.check_builtin_call (loc, arg_loc, fndecl,
+					   orig_fndecl, nargs, args));
+
+  gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL);
   switch (DECL_FUNCTION_CODE (fndecl))
     {
     case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index c266fee74c7..f14c2d8f84c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -803,7 +803,7 @@ extern void check_function_arguments_recurse (void (*)
 					      void *, tree,
 					      unsigned HOST_WIDE_INT);
 extern bool check_builtin_function_arguments (location_t, vec<location_t>,
-					      tree, int, tree *);
+					      tree, tree, int, tree *);
 extern void check_function_format (tree, int, tree *, vec<location_t> *);
 extern bool attribute_fallthrough_p (tree);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
@@ -979,7 +979,8 @@ extern bool c_switch_covers_all_cases_p (splay_tree, tree);
 extern tree build_function_call (location_t, tree, tree);
 
 extern tree build_function_call_vec (location_t, vec<location_t>, tree,
-				     vec<tree, va_gc> *, vec<tree, va_gc> *);
+				     vec<tree, va_gc> *, vec<tree, va_gc> *,
+				     tree = NULL_TREE);
 
 extern tree resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *);
 
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 90ae306c99a..a6d114a3330 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -2995,6 +2995,8 @@ inform_declaration (tree decl)
 }
 
 /* Build a function call to function FUNCTION with parameters PARAMS.
+   If FUNCTION is the result of resolving an overloaded target built-in,
+   ORIG_FUNDECL is the original function decl, otherwise it is null.
    ORIGTYPES, if not NULL, is a vector of types; each element is
    either NULL or the original type of the corresponding element in
    PARAMS.  The original type may differ from TREE_TYPE of the
@@ -3005,7 +3007,7 @@ inform_declaration (tree decl)
 tree
 build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 			 tree function, vec<tree, va_gc> *params,
-			 vec<tree, va_gc> *origtypes)
+			 vec<tree, va_gc> *origtypes, tree orig_fundecl)
 {
   tree fntype, fundecl = NULL_TREE;
   tree name = NULL_TREE, result;
@@ -3025,6 +3027,8 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
       if (flag_tm)
 	tm_malloc_replacement (function);
       fundecl = function;
+      if (!orig_fundecl)
+	orig_fundecl = fundecl;
       /* Atomic functions have type checking/casting already done.  They are 
 	 often rewritten and don't match the original parameter list.  */
       if (name && !strncmp (IDENTIFIER_POINTER (name), "__atomic_", 9))
@@ -3104,9 +3108,8 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
   /* Check that arguments to builtin functions match the expectations.  */
   if (fundecl
       && DECL_BUILT_IN (fundecl)
-      && DECL_BUILT_IN_CLASS (fundecl) == BUILT_IN_NORMAL
-      && !check_builtin_function_arguments (loc, arg_loc, fundecl, nargs,
-					    argarray))
+      && !check_builtin_function_arguments (loc, arg_loc, fundecl,
+					    orig_fundecl, nargs, argarray))
     return error_mark_node;
 
   /* Check that the arguments to the function are valid.  */
diff --git a/gcc/config/aarch64/aarch64-c.c b/gcc/config/aarch64/aarch64-c.c
index 84343baa239..959feca4a58 100644
--- a/gcc/config/aarch64/aarch64-c.c
+++ b/gcc/config/aarch64/aarch64-c.c
@@ -277,7 +277,28 @@ aarch64_resolve_overloaded_builtin (unsigned int uncast_location,
     return NULL_TREE;
   if (new_fndecl == error_mark_node)
     return error_mark_node;
-  return build_function_call_vec (location, vNULL, new_fndecl, arglist, NULL);
+  return build_function_call_vec (location, vNULL, new_fndecl, arglist,
+				  NULL, fndecl);
+}
+
+/* Implement TARGET_CHECK_BUILTIN_CALL.  */
+static bool
+aarch64_check_builtin_call (location_t loc, vec<location_t> arg_loc,
+			    tree fndecl, tree orig_fndecl,
+			    unsigned int nargs, tree *args)
+{
+  unsigned int code = DECL_FUNCTION_CODE (fndecl);
+  unsigned int subcode = code >> AARCH64_BUILTIN_SHIFT;
+  switch (code & AARCH64_BUILTIN_CLASS)
+    {
+    case AARCH64_BUILTIN_GENERAL:
+      return true;
+
+    case AARCH64_BUILTIN_SVE:
+      return aarch64_sve::check_builtin_call (loc, arg_loc, subcode,
+					      orig_fndecl, nargs, args);
+    }
+  gcc_unreachable ();
 }
 
 /* Implement REGISTER_TARGET_PRAGMAS.  */
@@ -289,6 +310,7 @@ aarch64_register_pragmas (void)
   targetm.target_option.pragma_parse = aarch64_pragma_target_parse;
 
   targetm.resolve_overloaded_builtin = aarch64_resolve_overloaded_builtin;
+  targetm.check_builtin_call = aarch64_check_builtin_call;
 
   c_register_pragma ("GCC", "aarch64", aarch64_pragma_aarch64);
 }
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index 57e151af1a9..77c788cb97a 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -548,6 +548,8 @@ namespace aarch64_sve {
   const char *mangle_builtin_type (const_tree);
   tree resolve_overloaded_builtin (location_t, unsigned int,
 				   vec<tree, va_gc> *);
+  bool check_builtin_call (location_t, vec<location_t>, unsigned int,
+			   tree, unsigned int, tree *);
   gimple *gimple_fold_builtin (unsigned int, gcall *);
   rtx expand_builtin (unsigned int, tree, rtx);
 }
diff --git a/gcc/config/aarch64/aarch64-sve-builtins.c b/gcc/config/aarch64/aarch64-sve-builtins.c
index 38f8f786e49..24fc2fc2f77 100644
--- a/gcc/config/aarch64/aarch64-sve-builtins.c
+++ b/gcc/config/aarch64/aarch64-sve-builtins.c
@@ -96,7 +96,13 @@ enum function_shape {
 
   /* sv<t0>_t svfoo[_t0](sv<t0>_t, sv<t0>_t)
      sv<t0>_t svfoo[_n_t0](sv<t0>_t, <t0>_t).  */
-  SHAPE_binary_opt_n
+  SHAPE_binary_opt_n,
+
+  /* sv<t0>_t svfoo[_n_t0])(sv<t0>_t, uint64_t)
+
+     The final argument must be an integer constant expression in the
+     range [1, <t0>_BITS].  */
+  SHAPE_shift_right_imm
 };
 
 /* Classifies an operation into "modes"; for example, to distinguish
@@ -258,6 +264,7 @@ private:
   void sig_inherent (const function_instance &, vec<tree> &);
   void sig_000 (const function_instance &, vec<tree> &);
   void sig_n_000 (const function_instance &, vec<tree> &);
+  void sig_n_00i (const function_instance &, vec<tree> &);
 
   void apply_predication (const function_instance &, vec<tree> &);
 
@@ -290,17 +297,22 @@ private:
 class function_resolver
 {
 public:
-  function_resolver (location_t, registered_function &, vec<tree, va_gc> &);
+  function_resolver (location_t, const registered_function &,
+		     vec<tree, va_gc> &);
   tree resolve ();
 
 private:
   tree resolve_uniform (unsigned int);
+  tree resolve_uniform_imm (unsigned int, unsigned int);
 
+  bool check_first_vector_argument (unsigned int, unsigned int &,
+				    unsigned int &, vector_type &);
   bool check_num_arguments (unsigned int);
   bool check_argument (unsigned int, vector_type);
   vector_type require_vector_type (unsigned int);
   bool require_matching_type (unsigned int, vector_type);
   bool scalar_argument_p (unsigned int);
+  bool require_integer_immediate (unsigned int);
   tree require_n_form (type_suffix, type_suffix = NUM_TYPE_SUFFIXES);
   tree require_form (function_mode, type_suffix,
 		     type_suffix = NUM_TYPE_SUFFIXES);
@@ -312,7 +324,7 @@ private:
   location_t m_location;
 
   /* The overloaded function.  */
-  registered_function &m_rfn;
+  const registered_function &m_rfn;
 
   /* The arguments to the overloaded function.  */
   vec<tree, va_gc> &m_arglist;
@@ -321,6 +333,39 @@ private:
   const function_group &m_group;
 };
 
+/* A class for checking that the semantic constraints on a function call are
+   satisfied, such as arguments being integer constant expressions with
+   a particular range.  */
+class function_checker
+{
+public:
+  function_checker (location_t, const function_instance &, tree,
+		    unsigned int, tree *);
+  bool check ();
+
+private:
+  bool check_shift_right_imm ();
+
+  bool require_immediate_range (unsigned int, HOST_WIDE_INT, HOST_WIDE_INT);
+
+  /* The location of the call.  */
+  location_t m_location;
+
+  /* The non-overloaded function being called.  */
+  const function_instance &m_fi;
+
+  /* The function that the user called (which might be an overloaded form
+     of M_FI).  */
+  tree m_decl;
+
+  /* The arguments to the function.  */
+  unsigned int m_nargs;
+  tree *m_args;
+
+  /* The static table entry for the function.  */
+  const function_group &m_group;
+};
+
 /* A class for folding a gimple function call.  */
 class gimple_folder
 {
@@ -350,6 +395,7 @@ public:
 
 private:
   rtx expand_add (unsigned int);
+  rtx expand_asrd ();
   rtx expand_max ();
   rtx expand_min ();
   rtx expand_ptrue ();
@@ -360,6 +406,9 @@ private:
   rtx expand_via_pred_direct_optab (optab, unsigned int, unsigned int);
   rtx expand_via_pred_insn (insn_code, unsigned int, unsigned int);
   rtx expand_via_pred_x_insn (insn_code, unsigned int);
+  rtx expand_pred_shift_right_imm (insn_code);
+
+  void require_immediate_range (unsigned int, HOST_WIDE_INT, HOST_WIDE_INT);
 
   bool try_negating_argument (unsigned int, machine_mode);
 
@@ -372,11 +421,15 @@ private:
   rtx generate_insn (insn_code);
 
   /* The function being called.  */
+  const registered_function &m_rfn;
   const function_instance &m_fi;
 
   /* The function call expression.  */
   tree m_exp;
 
+  /* The location of the call.  */
+  location_t m_location;
+
   /* Where the result should go, if convenient.  */
   rtx m_target;
 
@@ -507,6 +560,28 @@ find_vector_type (const_tree type)
   return NUM_VECTOR_TYPES;
 }
 
+/* Report that LOCATION has a call to DECL in which argument ARGNO
+   was not an integer constant expression.  */
+static void
+report_non_ice (location_t location, tree decl, unsigned int argno)
+{
+  error_at (location, "argument %d of %qE must be an integer constant"
+	    " expression", argno + 1, decl);
+}
+
+/* Report that LOCATION has a call to DECL in which argument ARGNO has
+   the value ACTUAL, whereas the function requires a value in the range
+   [MIN, MAX].  */
+static void
+report_out_of_range (location_t location, tree decl, unsigned int argno,
+		     HOST_WIDE_INT actual, HOST_WIDE_INT min,
+		     HOST_WIDE_INT max)
+{
+  error_at (location, "passing %wd to argument %d of %qE, which expects"
+	    " a value in the range [%wd, %wd]", actual, argno + 1, decl,
+	    min, max);
+}
+
 inline
 function_instance::function_instance (function func_in,
 				      function_mode mode_in,
@@ -613,6 +688,11 @@ arm_sve_h_builder::build (const function_group &group)
       /* No overloaded functions here.  */
       build_all (&arm_sve_h_builder::sig_inherent, group, MODE_none);
       break;
+
+    case SHAPE_shift_right_imm:
+      add_overloaded_functions (group, MODE_n);
+      build_all (&arm_sve_h_builder::sig_n_00i, group, MODE_n);
+      break;
     }
 }
 
@@ -668,6 +748,17 @@ arm_sve_h_builder::sig_n_000 (const function_instance &instance,
   types.quick_push (instance.scalar_type (0));
 }
 
+/* Describe the signature "sv<t0>_t svfoo[_n_t0](sv<t0>_t, uint64_t)"
+   for INSTANCE in TYPES.  */
+void
+arm_sve_h_builder::sig_n_00i (const function_instance &instance,
+			      vec<tree> &types)
+{
+  for (unsigned int i = 0; i < 2; ++i)
+    types.quick_push (instance.vector_type (0));
+  types.quick_push (scalar_types[VECTOR_TYPE_svuint64_t]);
+}
+
 /* If INSTANCE has a governing predicate, add it to the type signature
    in TYPES, where TYPES[0] is the return type, TYPES[1] is the first
    argument type, etc.  */
@@ -807,6 +898,7 @@ arm_sve_h_builder::get_attributes (const function_instance &instance)
   switch (instance.func)
     {
     case FUNC_svadd:
+    case FUNC_svasrd:
     case FUNC_svmax:
     case FUNC_svmin:
     case FUNC_svsub:
@@ -848,6 +940,7 @@ arm_sve_h_builder::get_explicit_types (function_shape shape)
     case SHAPE_inherent:
       return 1;
     case SHAPE_binary_opt_n:
+    case SHAPE_shift_right_imm:
       return 0;
     }
   gcc_unreachable ();
@@ -898,7 +991,7 @@ arm_sve_h_builder::finish_name ()
 }
 
 function_resolver::function_resolver (location_t location,
-				      registered_function &rfn,
+				      const registered_function &rfn,
 				      vec<tree, va_gc> &arglist)
   : m_location (location), m_rfn (rfn), m_arglist (arglist),
     m_group (function_groups[rfn.instance.func])
@@ -914,6 +1007,8 @@ function_resolver::resolve ()
     {
     case SHAPE_binary_opt_n:
       return resolve_uniform (2);
+    case SHAPE_shift_right_imm:
+      return resolve_uniform_imm (2, 1);
     case SHAPE_inherent:
       break;
     }
@@ -928,37 +1023,88 @@ tree
 function_resolver::resolve_uniform (unsigned int nops)
 {
   /* Check that we have the right number of arguments.  */
-  unsigned int full_nops = nops;
-  if (m_rfn.instance.pred != PRED_none)
-    full_nops += 1;
-  if (!check_num_arguments (full_nops))
+  unsigned int i, nargs;
+  vector_type type;
+  if (!check_first_vector_argument (nops, i, nargs, type))
+    return error_mark_node;
+
+  /* Handle subsequent arguments.  */
+  for (; i < nargs; ++i)
+    {
+      /* Allow the final argument to be scalar, if an _n form exists.  */
+      if (i == nargs - 1 && scalar_argument_p (i))
+	return require_n_form (get_type_suffix (type));
+      if (!require_matching_type (i, type))
+	return error_mark_node;
+    }
+  return require_form (m_rfn.instance.mode, get_type_suffix (type));
+}
+
+/* Like resolve_uniform, except that the final NIMM arguments have
+   type uint64_t and must be integer constant expressions.  */
+tree
+function_resolver::resolve_uniform_imm (unsigned int nops, unsigned int nimm)
+{
+  /* Check that we have the right number of arguments.  Also check the
+     first vector argument and governing predicate.  */
+  unsigned int i, nargs;
+  vector_type type;
+  if (!check_first_vector_argument (nops, i, nargs, type))
     return error_mark_node;
 
-  unsigned int i = 0;
+  /* Handle subsequent vector arguments.  */
+  for (; i < nargs - nimm; ++i)
+    if (!require_matching_type (i, type))
+      return error_mark_node;
+
+  /* Handle the final immediate arguments.  */
+  for (; i < nargs; ++i)
+    if (!require_integer_immediate (i))
+      return error_mark_node;
+
+  return require_form (m_rfn.instance.mode, get_type_suffix (type));
+}
+
+/* Check that the function is passed NOPS arguments plus the governing
+   predicate (if applicable) and that the first argument besides the
+   governing predicate is a vector.  Return true if so, otherwise
+   report a suitable error.
+
+   When returning true:
+   - set I to the number of the first unchecked argument (past the first
+     vector and past any governing predicate).
+   - set NARGS to the number of arguments including any governing
+     predicate.
+   - set TYPE to the type of the vector argument.  */
+bool
+function_resolver::check_first_vector_argument (unsigned int nops,
+						unsigned int &i,
+						unsigned int &nargs,
+						vector_type &type)
+{
+  i = 0;
+  nargs = nops;
+  type = NUM_VECTOR_TYPES;
+
+  if (m_rfn.instance.pred != PRED_none)
+    nargs += 1;
+  if (!check_num_arguments (nargs))
+    return false;
 
   /* Check the predicate argument.  */
   if (m_rfn.instance.pred != PRED_none)
     {
       if (!check_argument (i, VECTOR_TYPE_svbool_t))
-	return error_mark_node;
+	return false;
       i += 1;
     }
 
   /* The next argument is always a vector.  */
-  vector_type type = require_vector_type (i);
+  type = require_vector_type (i);
   if (type == NUM_VECTOR_TYPES)
-    return error_mark_node;
+    return false;
 
-  /* Handle subsequent arguments.  */
-  for (; i < full_nops; ++i)
-    {
-      /* Allow the final argument to be scalar, if an _n form exists.  */
-      if (i == full_nops - 1 && scalar_argument_p (i))
-	return require_n_form (get_type_suffix (type));
-      if (!require_matching_type (i, type))
-	return error_mark_node;
-    }
-  return require_form (m_rfn.instance.mode, get_type_suffix (type));
+  return true;
 }
 
 /* Require the function to have exactly EXPECTED arguments.  Return true
@@ -1035,6 +1181,20 @@ function_resolver::scalar_argument_p (unsigned int i)
   return INTEGRAL_TYPE_P (type) || SCALAR_FLOAT_TYPE_P (type);
 }
 
+/* Check that argument I has a suitable form for an integer constant
+   expression.  function_checker checks whether the argument is
+   actually constant and has a suitable range.  */
+bool
+function_resolver::require_integer_immediate (unsigned int i)
+{
+  if (!scalar_argument_p (i))
+    {
+      report_non_ice (m_location, m_rfn.decl, i);
+      return false;
+    }
+  return true;
+}
+
 /* Return the type of argument I, or error_mark_node if it isn't
    well-formed.  */
 tree
@@ -1101,12 +1261,72 @@ function_resolver::lookup_form (function_mode mode, type_suffix type0,
 {
   type_suffix_pair types = { type0, type1 };
   function_instance instance (m_rfn.instance.func, mode, types,
-					  m_rfn.instance.pred);
+			      m_rfn.instance.pred);
   registered_function *rfn
     = function_table->find_with_hash (instance, instance.hash ());
   return rfn ? rfn->decl : NULL_TREE;
 }
 
+function_checker::function_checker (location_t location,
+				    const function_instance &fi, tree decl,
+				    unsigned int nargs, tree *args)
+  : m_location (location), m_fi (fi), m_decl (decl), m_nargs (nargs),
+    m_args (args), m_group (function_groups[m_fi.func])
+{
+}
+
+/* Perform semantic checks on the call.  Return true if the call is valid,
+   otherwise report a suitable error.  */
+bool
+function_checker::check ()
+{
+  switch (m_group.shape)
+    {
+    case SHAPE_shift_right_imm:
+      return check_shift_right_imm ();
+
+    case SHAPE_inherent:
+    case SHAPE_binary_opt_n:
+      return true;
+    }
+  gcc_unreachable ();
+}
+
+/* Check a SHAPE_shift_right_imm call.  */
+bool
+function_checker::check_shift_right_imm ()
+{
+  unsigned int bits = type_suffixes[m_fi.types[0]].elem_bits;
+  return require_immediate_range (2, 1, bits);
+}
+
+/* Check that argument ARGNO is an integer constant expression in the
+   range [MIN, MAX].  */
+bool
+function_checker::require_immediate_range (unsigned int argno,
+					   HOST_WIDE_INT min,
+					   HOST_WIDE_INT max)
+{
+  if (m_nargs <= argno)
+    return true;
+
+  tree arg = m_args[argno];
+  if (!tree_fits_shwi_p (arg))
+    {
+      report_non_ice (m_location, m_decl, argno);
+      return false;
+    }
+
+  HOST_WIDE_INT actual = tree_to_shwi (arg);
+  if (!IN_RANGE (actual, min, max))
+    {
+      report_out_of_range (m_location, m_decl, argno, actual, min, max);
+      return false;
+    }
+
+  return true;
+}
+
 /* Register the built-in SVE ABI types, such as __SVBool_t.  */
 static void
 register_builtin_types ()
@@ -1190,13 +1410,10 @@ builtin_decl (unsigned int code, bool)
   return (*registered_functions)[code]->decl;
 }
 
-/* Check a call to the SVE function with subcode CODE.  The call occurs
-   at location LOCATION and has the arguments given by ARGLIST.
-
-   Perform any extra semantic checks, such as testing for integer constant
-   expressions.  If we're implementing manual overloading and the
-   function is overloaded, attempt to determine the corresponding
-   non-overloaded function.
+/* If we're implementing manual overloading, check whether the SVE
+   function with subcode CODE is overloaded, and if so attempt to
+   determine the corresponding non-overloaded function.  The call
+   occurs at location LOCATION and has the arguments given by ARGLIST.
 
    If the call is erroneous, report an appropriate error and return
    error_mark_node.  Otherwise, if the function is overloaded, return
@@ -1215,6 +1432,23 @@ resolve_overloaded_builtin (location_t location, unsigned int code,
   return NULL_TREE;
 }
 
+/* Perform any semantic checks needed for a call to the SVE function with
+   subcode CODE, such as testing for integer constant expressions.
+   The call occurs at location LOCATION and has NARGS arguments.
+   ARGS gives the value of each argument and ARG_LOCATION gives
+   their location.  FNDECL is the original function decl, before
+   overload resolution.
+
+   Return true if the call is valid, otherwise report a suitable error.  */
+bool
+check_builtin_call (location_t location, vec<location_t>, unsigned int code,
+		    tree fndecl, unsigned int nargs, tree *args)
+{
+  registered_function &rfn = *(*registered_functions)[code];
+  return function_checker (location, rfn.instance, fndecl,
+			   nargs, args).check ();
+}
+
 /* Construct a folder for CALL, which calls the SVE function with
    subcode CODE.  */
 gimple_folder::gimple_folder (unsigned int code, gcall *call)
@@ -1231,6 +1465,7 @@ gimple_folder::fold ()
   switch (m_fi.func)
     {
     case FUNC_svadd:
+    case FUNC_svasrd:
     case FUNC_svmax:
     case FUNC_svmin:
     case FUNC_svsub:
@@ -1274,8 +1509,9 @@ gimple_fold_builtin (unsigned int code, gcall *stmt)
    EXP is the call expression and TARGET is the preferred location for
    the result.  */
 function_expander::function_expander (unsigned int code, tree exp, rtx target)
-  : m_fi ((*registered_functions)[code]->instance),
-    m_exp (exp), m_target (target)
+  : m_rfn (*(*registered_functions)[code]),
+    m_fi (m_rfn.instance),
+    m_exp (exp), m_location (EXPR_LOCATION (exp)), m_target (target)
 {
 }
 
@@ -1293,6 +1529,9 @@ function_expander::expand ()
     case FUNC_svadd:
       return expand_add (1);
 
+    case FUNC_svasrd:
+      return expand_asrd ();
+
     case FUNC_svmax:
       return expand_max ();
 
@@ -1335,6 +1574,13 @@ function_expander::expand_add (unsigned int merge_argno)
   return expand_via_pred_direct_optab (cond_add_optab, 2, merge_argno);
 }
 
+/* Expand a call to svasrd.  */
+rtx
+function_expander::expand_asrd ()
+{
+  return expand_pred_shift_right_imm (code_for_cond_asrd (get_mode (0)));
+}
+
 /* Expand a call to svmax.  */
 rtx
 function_expander::expand_max ()
@@ -1428,7 +1674,7 @@ function_expander::expand_via_pred_direct_optab (optab op, unsigned int nops,
    Merging forms use argument MERGE_ARGNO as the fallback value.  */
 rtx
 function_expander::expand_via_pred_insn (insn_code icode, unsigned int nops,
-						 unsigned int merge_argno)
+					 unsigned int merge_argno)
 {
   machine_mode mode = get_mode (0);
   machine_mode pred_mode = get_pred_mode (0);
@@ -1529,6 +1775,36 @@ function_expander::expand_signed_pred_op (rtx_code code_for_sint,
     }
 }
 
+/* Expand a call to a SHAPE_shift_right_imm function using predicated
+   instruction ICODE, which has the same operand order as conditional
+   optabs like cond_add_optab.  */
+rtx
+function_expander::expand_pred_shift_right_imm (insn_code icode)
+{
+  require_immediate_range (2, 1, GET_MODE_UNIT_BITSIZE (get_mode (0)));
+  return expand_via_pred_insn (icode, 2, 1);
+}
+
+/* Require that argument ARGNO is a constant integer in the range
+   [MIN, MAX].  Report an appropriate error if it isn't and set
+   the argument to a safe in-range value.  */
+void
+function_expander::require_immediate_range (unsigned int argno,
+					    HOST_WIDE_INT min,
+					    HOST_WIDE_INT max)
+{
+  if (!CONST_INT_P (m_args[argno]))
+    report_non_ice (m_location, m_rfn.decl, argno);
+  else
+    {
+      HOST_WIDE_INT actual = INTVAL (m_args[argno]);
+      if (IN_RANGE (actual, min, max))
+	return;
+      report_out_of_range (m_location, m_rfn.decl, argno, actual, min, max);
+    }
+  m_args[argno] = GEN_INT (min);
+}
+
 /* Return true if argument I is a constant argument that can be negated
    at compile time, replacing it with the negated value if so.  MODE is the
    associated vector mode, but the argument could be a single element.  */
diff --git a/gcc/config/aarch64/aarch64-sve-builtins.def b/gcc/config/aarch64/aarch64-sve-builtins.def
index ae53dc7f369..d53ae4b10d9 100644
--- a/gcc/config/aarch64/aarch64-sve-builtins.def
+++ b/gcc/config/aarch64/aarch64-sve-builtins.def
@@ -61,6 +61,7 @@ DEF_SVE_TYPE_SUFFIX (u64, svuint64_t, 64)
 
 /* List of functions, in alphabetical order.  */
 DEF_SVE_FUNCTION (svadd, binary_opt_n, all_data, mxz)
+DEF_SVE_FUNCTION (svasrd, shift_right_imm, all_signed, mxz)
 DEF_SVE_FUNCTION (svmax, binary_opt_n, all_data, mxz)
 DEF_SVE_FUNCTION (svmin, binary_opt_n, all_data, mxz)
 DEF_SVE_FUNCTION (svptrue, inherent, all_pred, none)
diff --git a/gcc/config/aarch64/aarch64-sve.md b/gcc/config/aarch64/aarch64-sve.md
index d6f8a6f63d0..558c58e42c5 100644
--- a/gcc/config/aarch64/aarch64-sve.md
+++ b/gcc/config/aarch64/aarch64-sve.md
@@ -1995,6 +1995,52 @@
   "#"
 )
 
+;; Predicated ASRD.
+(define_expand "@cond_asrd<mode>"
+  [(set (match_operand:SVE_I 0 "register_operand")
+	(unspec:SVE_I
+	  [(match_operand:<VPRED> 1 "register_operand")
+	   (unspec:SVE_I
+	     [(match_operand:SVE_I 2 "register_operand")
+	      (match_operand:SVE_I 3 "aarch64_simd_rshift_imm")]
+	     UNSPEC_ASRD)
+	   (match_operand:SVE_I 4 "aarch64_simd_reg_or_zero")]
+	  UNSPEC_SEL))]
+  "TARGET_SVE"
+)
+
+;; Predicated ASRD with select matching the first input.
+(define_insn "*cond_asrd<mode>_2"
+  [(set (match_operand:SVE_I 0 "register_operand" "=w, ?&w")
+	(unspec:SVE_I
+	  [(match_operand:<VPRED> 1 "register_operand" "Upl, Upl")
+	   (unspec:SVE_I
+	     [(match_operand:SVE_I 2 "register_operand" "0, w")
+	      (match_operand:SVE_I 3 "aarch64_simd_rshift_imm")]
+	     UNSPEC_ASRD)
+	   (match_dup 2)]
+	  UNSPEC_SEL))]
+  "TARGET_SVE"
+  "@
+   asrd\t%0.<Vetype>, %1/m, %0.<Vetype>, #%3
+   movprfx\t%0, %2\;asrd\t%0.<Vetype>, %1/m, %0.<Vetype>, #%3"
+  [(set_attr "movprfx" "*,yes")])
+
+;; Predicated ASRD with select matching zero.
+(define_insn "*cond_asrd<mode>_z"
+  [(set (match_operand:SVE_I 0 "register_operand" "=w")
+	(unspec:SVE_I
+	  [(match_operand:<VPRED> 1 "register_operand" "Upl")
+	   (unspec:SVE_I
+	     [(match_operand:SVE_I 2 "register_operand" "w")
+	      (match_operand:SVE_I 3 "aarch64_simd_rshift_imm")]
+	     UNSPEC_ASRD)
+	   (match_operand:SVE_I 4 "aarch64_simd_imm_zero")]
+	  UNSPEC_SEL))]
+  "TARGET_SVE"
+  "movprfx\t%0.<Vetype>, %1/z, %2.<Vetype>\;asrd\t%0.<Vetype>, %1/m, %0.<Vetype>, #%3"
+  [(set_attr "movprfx" "yes")])
+
 (define_insn "*cond_<optab><mode>_any"
   [(set (match_operand:SVE_SDI 0 "register_operand" "=&w")
 	(unspec:SVE_SDI
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 9880f8f0fff..cfbe52b593b 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -195,6 +195,7 @@
     UNSPEC_CLASTB
     UNSPEC_FADDA
     UNSPEC_REV_SUBREG
+    UNSPEC_ASRD
 ])
 
 (define_c_enum "unspecv" [
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 209c1fd2f0e..2bfe977513a 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8812,12 +8812,14 @@ maybe_warn_class_memaccess (location_t loc, tree fndecl,
 }
 
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
+   If FN is the result of resolving an overloaded target built-in,
+   ORIG_FNDECL is the original function decl, otherwise it is null.
    This function performs no overload resolution, conversion, or other
    high-level operations.  */
 
 tree
 build_cxx_call (tree fn, int nargs, tree *argarray,
-		tsubst_flags_t complain)
+		tsubst_flags_t complain, tree orig_fndecl)
 {
   tree fndecl;
 
@@ -8827,12 +8829,13 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
   SET_EXPR_LOCATION (fn, loc);
 
   fndecl = get_callee_fndecl (fn);
+  if (!orig_fndecl)
+    orig_fndecl = fndecl;
 
   /* Check that arguments to builtin functions match the expectations.  */
   if (fndecl
       && !processing_template_decl
-      && DECL_BUILT_IN (fndecl)
-      && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+      && DECL_BUILT_IN (fndecl))
     {
       int i;
 
@@ -8842,7 +8845,7 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
 	argarray[i] = maybe_constant_value (argarray[i]);
 
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
-					     nargs, argarray))
+					     orig_fndecl, nargs, argarray))
 	return error_mark_node;
     }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6c43a9656a8..c8ff82b14e7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6094,7 +6094,8 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
 extern tree in_charge_arg_for_name		(tree);
 extern tree build_cxx_call			(tree, int, tree *,
-						 tsubst_flags_t);
+						 tsubst_flags_t,
+						 tree = NULL_TREE);
 extern bool is_std_init_list			(tree);
 extern bool is_list_ctor			(tree);
 extern void validate_conversion_obstack		(void);
@@ -7215,7 +7216,8 @@ extern tree get_member_function_from_ptrfunc	(tree *, tree, tsubst_flags_t);
 extern tree cp_build_function_call_nary         (tree, tsubst_flags_t, ...)
 						ATTRIBUTE_SENTINEL;
 extern tree cp_build_function_call_vec		(tree, vec<tree, va_gc> **,
-						 tsubst_flags_t);
+						 tsubst_flags_t,
+						 tree = NULL_TREE);
 extern tree build_x_binary_op			(location_t,
 						 enum tree_code, tree,
 						 enum tree_code, tree,
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index ea4ce9649cd..17be052b913 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -3605,11 +3605,11 @@ build_function_call (location_t /*loc*/,
 tree
 build_function_call_vec (location_t /*loc*/, vec<location_t> /*arg_loc*/,
 			 tree function, vec<tree, va_gc> *params,
-			 vec<tree, va_gc> * /*origtypes*/)
+			 vec<tree, va_gc> * /*origtypes*/, tree orig_function)
 {
   vec<tree, va_gc> *orig_params = params;
   tree ret = cp_build_function_call_vec (function, &params,
-					 tf_warning_or_error);
+					 tf_warning_or_error, orig_function);
 
   /* cp_build_function_call_vec can reallocate PARAMS by adding
      default arguments.  That should never happen here.  Verify
@@ -3654,13 +3654,15 @@ cp_build_function_call_nary (tree function, tsubst_flags_t complain, ...)
   return ret;
 }
 
-/* Build a function call using a vector of arguments.  PARAMS may be
-   NULL if there are no parameters.  This changes the contents of
-   PARAMS.  */
+/* Build a function call using a vector of arguments.
+   If FUNCTION is the result of resolving an overloaded target built-in,
+   ORIG_FNDECL is the original function decl, otherwise it is null.
+   PARAMS may be NULL if there are no parameters.  This changes the
+   contents of PARAMS.  */
 
 tree
 cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
-			    tsubst_flags_t complain)
+			    tsubst_flags_t complain, tree orig_fndecl)
 {
   tree fntype, fndecl;
   int is_method;
@@ -3784,7 +3786,7 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
   bool warned_p = check_function_arguments (input_location, fndecl, fntype,
 					    nargs, argarray, NULL);
 
-  ret = build_cxx_call (function, nargs, argarray, complain);
+  ret = build_cxx_call (function, nargs, argarray, complain, orig_fndecl);
 
   if (warned_p)
     {
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index ff6d5146010..7f5c0f066aa 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11446,6 +11446,21 @@ another @code{CALL_EXPR}.
 @var{arglist} really has type @samp{VEC(tree,gc)*}
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_CHECK_BUILTIN_CALL (location_t @var{loc}, vec<location_t> @var{arg_loc}, tree @var{fndecl}, tree @var{orig_fndecl}, unsigned int @var{nargs}, tree *@var{args})
+Perform semantic checking on a call to a machine-specific built-in
+function after its arguments have been constrained to the function
+signature.  Return true if the call is valid, otherwise report an error
+and return false.
+
+This hook is called after @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}.
+The call was originally to built-in function @var{orig_fndecl},
+but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}
+step is now to built-in function @var{fndecl}.  @var{loc} is the
+location of the call and @var{args} is an array of function arguments,
+of which there are @var{nargs}.  @var{arg_loc} specifies the location
+of each argument.
+@end deftypefn
+
 @deftypefn {Target Hook} tree TARGET_FOLD_BUILTIN (tree @var{fndecl}, int @var{n_args}, tree *@var{argp}, bool @var{ignore})
 Fold a call to a machine specific built-in function that was set up by
 @samp{TARGET_INIT_BUILTINS}.  @var{fndecl} is the declaration of the
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 2f97151f341..7df9d4d2af6 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -7869,6 +7869,8 @@ to by @var{ce_info}.
 
 @hook TARGET_RESOLVE_OVERLOADED_BUILTIN
 
+@hook TARGET_CHECK_BUILTIN_CALL
+
 @hook TARGET_FOLD_BUILTIN
 
 @hook TARGET_GIMPLE_FOLD_BUILTIN
diff --git a/gcc/target.def b/gcc/target.def
index ff89e72dd2b..ca6d5db98b7 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2406,6 +2406,24 @@ another @code{CALL_EXPR}.\n\
 @var{arglist} really has type @samp{VEC(tree,gc)*}",
  tree, (unsigned int /*location_t*/ loc, tree fndecl, void *arglist), NULL)
 
+DEFHOOK
+(check_builtin_call,
+ "Perform semantic checking on a call to a machine-specific built-in\n\
+function after its arguments have been constrained to the function\n\
+signature.  Return true if the call is valid, otherwise report an error\n\
+and return false.\n\
+\n\
+This hook is called after @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}.\n\
+The call was originally to built-in function @var{orig_fndecl},\n\
+but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}\n\
+step is now to built-in function @var{fndecl}.  @var{loc} is the\n\
+location of the call and @var{args} is an array of function arguments,\n\
+of which there are @var{nargs}.  @var{arg_loc} specifies the location\n\
+of each argument.",
+ bool, (location_t loc, vec<location_t> arg_loc, tree fndecl,
+	tree orig_fndecl, unsigned int nargs, tree *args),
+ NULL)
+
 /* Fold a target-specific builtin to a tree valid for both GIMPLE
    and GENERIC.  */
 DEFHOOK
diff --git a/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_1.C b/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_1.C
new file mode 100644
index 00000000000..a73934f5668
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_1.C
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c++11 -Wall -Wextra" } */
+
+#include <arm_sve.h>
+
+constexpr uint64_t const_add (uint64_t a, uint64_t b) { return a + b; }
+uint64_t add (uint64_t a, uint64_t b) { return a + b; }
+
+void
+f1 (svbool_t pg, svuint8_t u8, svint8_t s8, svint16_t s16,
+    svint32_t s32, svint64_t s64, int x)
+{
+  const int one = 1;
+  u8 = svasrd_x (pg, u8, 1); /* { dg-error {no matching function for call to 'svasrd_x\(svbool_t&, svuint8_t&, [^)]*\)'} } */
+  s8 = svasrd_x (pg, s8, x); /* { dg-error "argument 3 of 'svasrd_x' must be an integer constant expression" } */
+  s8 = svasrd_x (pg, s8, one);
+  s8 = svasrd_x (pg, s8, 0.4); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_x (pg, s8, 1.0);
+  s8 = svasrd_x (pg, s8, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_x (pg, s8, 1);
+  s8 = svasrd_x (pg, s8, 1 + 1);
+  s8 = svasrd_x (pg, s8, const_add (1, 1));
+  s8 = svasrd_x (pg, s8, add (1, 1)); /* { dg-error "argument 3 of 'svasrd_x' must be an integer constant expression" } */
+  s8 = svasrd_x (pg, s8, 8);
+  s8 = svasrd_x (pg, s8, 9); /* { dg-error {passing 9 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_x (pg, s8, (uint64_t (1) << 62) + 1); /* { dg-error {passing [^ ]* to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s16 = svasrd_x (pg, s16, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 16\]} } */
+  s16 = svasrd_x (pg, s16, 1);
+  s16 = svasrd_x (pg, s16, 16);
+  s16 = svasrd_x (pg, s16, 17); /* { dg-error {passing 17 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 16\]} } */
+  s32 = svasrd_x (pg, s32, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 32\]} } */
+  s32 = svasrd_x (pg, s32, 1);
+  s32 = svasrd_x (pg, s32, 32);
+  s32 = svasrd_x (pg, s32, 33); /* { dg-error {passing 33 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 32\]} } */
+  s64 = svasrd_x (pg, s64, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 64\]} } */
+  s64 = svasrd_x (pg, s64, 1);
+  s64 = svasrd_x (pg, s64, 64);
+  s64 = svasrd_x (pg, s64, 65); /* { dg-error {passing 65 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 64\]} } */
+}
diff --git a/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_2.C b/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_2.C
new file mode 100644
index 00000000000..bbe7ba72bfa
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_2.C
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c++11 -Wall -Wextra" } */
+
+#include <arm_sve.h>
+
+constexpr uint64_t const_add (uint64_t a, uint64_t b) { return a + b; }
+uint64_t add (uint64_t a, uint64_t b) { return a + b; }
+
+void
+f1 (svbool_t pg, svint8_t s8, svint16_t s16, svint32_t s32, svint64_t s64,
+    int x)
+{
+  const int one = 1;
+  s8 = svasrd_n_s8_x (pg, s8, x); /* { dg-error "argument 3 of 'svasrd_n_s8_x' must be an integer constant expression" } */
+  s8 = svasrd_n_s8_x (pg, s8, one);
+  s8 = svasrd_n_s8_x (pg, s8, 0.4); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_n_s8_x (pg, s8, 1.0);
+  s8 = svasrd_n_s8_x (pg, s8, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_n_s8_x (pg, s8, 1);
+  s8 = svasrd_n_s8_x (pg, s8, 1 + 1);
+  s8 = svasrd_n_s8_x (pg, s8, const_add (1, 1));
+  s8 = svasrd_n_s8_x (pg, s8, add (1, 1)); /* { dg-error "argument 3 of 'svasrd_n_s8_x' must be an integer constant expression" } */
+  s8 = svasrd_n_s8_x (pg, s8, 8);
+  s8 = svasrd_n_s8_x (pg, s8, 9); /* { dg-error {passing 9 to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_n_s8_x (pg, s8, (uint64_t (1) << 62) + 1); /* { dg-error {passing [^ ]* to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s16 = svasrd_n_s16_x (pg, s16, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s16_x', which expects a value in the range \[1, 16\]} } */
+  s16 = svasrd_n_s16_x (pg, s16, 1);
+  s16 = svasrd_n_s16_x (pg, s16, 16);
+  s16 = svasrd_n_s16_x (pg, s16, 17); /* { dg-error {passing 17 to argument 3 of 'svasrd_n_s16_x', which expects a value in the range \[1, 16\]} } */
+  s32 = svasrd_n_s32_x (pg, s32, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s32_x', which expects a value in the range \[1, 32\]} } */
+  s32 = svasrd_n_s32_x (pg, s32, 1);
+  s32 = svasrd_n_s32_x (pg, s32, 32);
+  s32 = svasrd_n_s32_x (pg, s32, 33); /* { dg-error {passing 33 to argument 3 of 'svasrd_n_s32_x', which expects a value in the range \[1, 32\]} } */
+  s64 = svasrd_n_s64_x (pg, s64, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s64_x', which expects a value in the range \[1, 64\]} } */
+  s64 = svasrd_n_s64_x (pg, s64, 1);
+  s64 = svasrd_n_s64_x (pg, s64, 64);
+  s64 = svasrd_n_s64_x (pg, s64, 65); /* { dg-error {passing 65 to argument 3 of 'svasrd_n_s64_x', which expects a value in the range \[1, 64\]} } */
+}
diff --git a/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_3.C b/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_3.C
new file mode 100644
index 00000000000..5ebd770b272
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/sve-acle/general-c++/asrd_3.C
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c++11 -Wall -Wextra" } */
+
+#include <arm_sve.h>
+
+constexpr uint64_t const_add (uint64_t a, uint64_t b) { return a + b; }
+uint64_t add (uint64_t a, uint64_t b) { return a + b; }
+
+template<uint64_t N, typename T>
+T shift (svbool_t pg, T v) { return svasrd_x (pg, v, N); }
+/* { dg-error {no matching function for call to 'svasrd_x\(svbool_t&,} "" { target *-*-* } .-1 } */
+/* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} "" { target *-*-* } .-2 } */
+/* { dg-error {passing 9 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} "" { target *-*-* } .-3 } */
+/* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 16\]} "" { target *-*-* } .-4 } */
+/* { dg-error {passing 17 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 16\]} "" { target *-*-* } .-5 } */
+/* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 32\]} "" { target *-*-* } .-6 } */
+/* { dg-error {passing 33 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 32\]} "" { target *-*-* } .-7 } */
+/* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 64\]} "" { target *-*-* } .-8 } */
+/* { dg-error {passing 65 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 64\]} "" { target *-*-* } .-9 } */
+
+template<typename T>
+T shift1 (svbool_t pg, T v, uint64_t n) { return svasrd_x (pg, v, n); }
+
+template<typename T>
+T shift2 (svbool_t pg, T v, uint64_t n) { return svasrd_x (pg, v, n); }
+/* { dg-error {argument 3 of 'svasrd_x' must be an integer constant expression} "" { target *-*-* } .-1 } */
+
+void
+f1 (svbool_t pg, svuint8_t u8, svint8_t s8, svint16_t s16,
+    svint32_t s32, svint64_t s64)
+{
+  u8 = shift <1> (pg, u8);
+  s8 = shift <0> (pg, s8);
+  s8 = shift <1> (pg, s8);
+  s8 = shift <8> (pg, s8);
+  s8 = shift <9> (pg, s8);
+  s16 = shift <0> (pg, s16);
+  s16 = shift <1> (pg, s16);
+  s16 = shift <16> (pg, s16);
+  s16 = shift <17> (pg, s16);
+  s32 = shift <0> (pg, s32);
+  s32 = shift <1> (pg, s32);
+  s32 = shift <32> (pg, s32);
+  s32 = shift <33> (pg, s32);
+  s64 = shift <0> (pg, s64);
+  s64 = shift <1> (pg, s64);
+  s64 = shift <64> (pg, s64);
+  s64 = shift <65> (pg, s64);
+
+  s8 = shift2 (pg, s8, 1);
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s16.c b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s16.c
new file mode 100644
index 00000000000..6cad5d98aa9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s16.c
@@ -0,0 +1,178 @@
+/* { dg-do compile } */
+/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */
+
+#include "test_sve_acle.h"
+
+/*
+** asrd_1_s16_m_tied1:
+**	asrd	z0\.h, p0/m, z0\.h, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s16_m_tied1, svint16_t,
+		z0 = svasrd_n_s16_m (p0, z0, 1),
+		z0 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_1_s16_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.h, p0/m, z1\.h, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s16_m_untied, svint16_t,
+		z1 = svasrd_n_s16_m (p0, z0, 1),
+		z1 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_2_s16_m_tied1:
+**	asrd	z0\.h, p0/m, z0\.h, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s16_m_tied1, svint16_t,
+		z0 = svasrd_n_s16_m (p0, z0, 2),
+		z0 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_2_s16_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.h, p0/m, z1\.h, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s16_m_untied, svint16_t,
+		z1 = svasrd_n_s16_m (p0, z0, 2),
+		z1 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_16_s16_m_tied1:
+**	asrd	z0\.h, p0/m, z0\.h, #16
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_16_s16_m_tied1, svint16_t,
+		z0 = svasrd_n_s16_m (p0, z0, 16),
+		z0 = svasrd_m (p0, z0, 16))
+
+/*
+** asrd_16_s16_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.h, p0/m, z1\.h, #16
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_16_s16_m_untied, svint16_t,
+		z1 = svasrd_n_s16_m (p0, z0, 16),
+		z1 = svasrd_m (p0, z0, 16))
+
+/*
+** asrd_1_s16_z_tied1:
+**	movprfx	z0\.h, p0/z, z0\.h
+**	asrd	z0\.h, p0/m, z0\.h, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s16_z_tied1, svint16_t,
+		z0 = svasrd_n_s16_z (p0, z0, 1),
+		z0 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_1_s16_z_untied:
+**	movprfx	z1\.h, p0/z, z0\.h
+**	asrd	z1\.h, p0/m, z1\.h, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s16_z_untied, svint16_t,
+		z1 = svasrd_n_s16_z (p0, z0, 1),
+		z1 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_2_s16_z_tied1:
+**	movprfx	z0\.h, p0/z, z0\.h
+**	asrd	z0\.h, p0/m, z0\.h, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s16_z_tied1, svint16_t,
+		z0 = svasrd_n_s16_z (p0, z0, 2),
+		z0 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_2_s16_z_untied:
+**	movprfx	z1\.h, p0/z, z0\.h
+**	asrd	z1\.h, p0/m, z1\.h, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s16_z_untied, svint16_t,
+		z1 = svasrd_n_s16_z (p0, z0, 2),
+		z1 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_16_s16_z_tied1:
+**	movprfx	z0\.h, p0/z, z0\.h
+**	asrd	z0\.h, p0/m, z0\.h, #16
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_16_s16_z_tied1, svint16_t,
+		z0 = svasrd_n_s16_z (p0, z0, 16),
+		z0 = svasrd_z (p0, z0, 16))
+
+/*
+** asrd_16_s16_z_untied:
+**	movprfx	z1\.h, p0/z, z0\.h
+**	asrd	z1\.h, p0/m, z1\.h, #16
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_16_s16_z_untied, svint16_t,
+		z1 = svasrd_n_s16_z (p0, z0, 16),
+		z1 = svasrd_z (p0, z0, 16))
+
+/*
+** asrd_1_s16_x_tied1:
+**	asrd	z0\.h, p0/m, z0\.h, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s16_x_tied1, svint16_t,
+		z0 = svasrd_n_s16_x (p0, z0, 1),
+		z0 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_1_s16_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.h, p0/m, z1\.h, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s16_x_untied, svint16_t,
+		z1 = svasrd_n_s16_x (p0, z0, 1),
+		z1 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_2_s16_x_tied1:
+**	asrd	z0\.h, p0/m, z0\.h, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s16_x_tied1, svint16_t,
+		z0 = svasrd_n_s16_x (p0, z0, 2),
+		z0 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_2_s16_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.h, p0/m, z1\.h, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s16_x_untied, svint16_t,
+		z1 = svasrd_n_s16_x (p0, z0, 2),
+		z1 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_16_s16_x_tied1:
+**	asrd	z0\.h, p0/m, z0\.h, #16
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_16_s16_x_tied1, svint16_t,
+		z0 = svasrd_n_s16_x (p0, z0, 16),
+		z0 = svasrd_x (p0, z0, 16))
+
+/*
+** asrd_16_s16_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.h, p0/m, z1\.h, #16
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_16_s16_x_untied, svint16_t,
+		z1 = svasrd_n_s16_x (p0, z0, 16),
+		z1 = svasrd_x (p0, z0, 16))
diff --git a/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s32.c b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s32.c
new file mode 100644
index 00000000000..9187e554b4f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s32.c
@@ -0,0 +1,178 @@
+/* { dg-do compile } */
+/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */
+
+#include "test_sve_acle.h"
+
+/*
+** asrd_1_s32_m_tied1:
+**	asrd	z0\.s, p0/m, z0\.s, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s32_m_tied1, svint32_t,
+		z0 = svasrd_n_s32_m (p0, z0, 1),
+		z0 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_1_s32_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.s, p0/m, z1\.s, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s32_m_untied, svint32_t,
+		z1 = svasrd_n_s32_m (p0, z0, 1),
+		z1 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_2_s32_m_tied1:
+**	asrd	z0\.s, p0/m, z0\.s, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s32_m_tied1, svint32_t,
+		z0 = svasrd_n_s32_m (p0, z0, 2),
+		z0 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_2_s32_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.s, p0/m, z1\.s, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s32_m_untied, svint32_t,
+		z1 = svasrd_n_s32_m (p0, z0, 2),
+		z1 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_32_s32_m_tied1:
+**	asrd	z0\.s, p0/m, z0\.s, #32
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_32_s32_m_tied1, svint32_t,
+		z0 = svasrd_n_s32_m (p0, z0, 32),
+		z0 = svasrd_m (p0, z0, 32))
+
+/*
+** asrd_32_s32_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.s, p0/m, z1\.s, #32
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_32_s32_m_untied, svint32_t,
+		z1 = svasrd_n_s32_m (p0, z0, 32),
+		z1 = svasrd_m (p0, z0, 32))
+
+/*
+** asrd_1_s32_z_tied1:
+**	movprfx	z0\.s, p0/z, z0\.s
+**	asrd	z0\.s, p0/m, z0\.s, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s32_z_tied1, svint32_t,
+		z0 = svasrd_n_s32_z (p0, z0, 1),
+		z0 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_1_s32_z_untied:
+**	movprfx	z1\.s, p0/z, z0\.s
+**	asrd	z1\.s, p0/m, z1\.s, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s32_z_untied, svint32_t,
+		z1 = svasrd_n_s32_z (p0, z0, 1),
+		z1 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_2_s32_z_tied1:
+**	movprfx	z0\.s, p0/z, z0\.s
+**	asrd	z0\.s, p0/m, z0\.s, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s32_z_tied1, svint32_t,
+		z0 = svasrd_n_s32_z (p0, z0, 2),
+		z0 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_2_s32_z_untied:
+**	movprfx	z1\.s, p0/z, z0\.s
+**	asrd	z1\.s, p0/m, z1\.s, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s32_z_untied, svint32_t,
+		z1 = svasrd_n_s32_z (p0, z0, 2),
+		z1 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_32_s32_z_tied1:
+**	movprfx	z0\.s, p0/z, z0\.s
+**	asrd	z0\.s, p0/m, z0\.s, #32
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_32_s32_z_tied1, svint32_t,
+		z0 = svasrd_n_s32_z (p0, z0, 32),
+		z0 = svasrd_z (p0, z0, 32))
+
+/*
+** asrd_32_s32_z_untied:
+**	movprfx	z1\.s, p0/z, z0\.s
+**	asrd	z1\.s, p0/m, z1\.s, #32
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_32_s32_z_untied, svint32_t,
+		z1 = svasrd_n_s32_z (p0, z0, 32),
+		z1 = svasrd_z (p0, z0, 32))
+
+/*
+** asrd_1_s32_x_tied1:
+**	asrd	z0\.s, p0/m, z0\.s, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s32_x_tied1, svint32_t,
+		z0 = svasrd_n_s32_x (p0, z0, 1),
+		z0 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_1_s32_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.s, p0/m, z1\.s, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s32_x_untied, svint32_t,
+		z1 = svasrd_n_s32_x (p0, z0, 1),
+		z1 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_2_s32_x_tied1:
+**	asrd	z0\.s, p0/m, z0\.s, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s32_x_tied1, svint32_t,
+		z0 = svasrd_n_s32_x (p0, z0, 2),
+		z0 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_2_s32_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.s, p0/m, z1\.s, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s32_x_untied, svint32_t,
+		z1 = svasrd_n_s32_x (p0, z0, 2),
+		z1 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_32_s32_x_tied1:
+**	asrd	z0\.s, p0/m, z0\.s, #32
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_32_s32_x_tied1, svint32_t,
+		z0 = svasrd_n_s32_x (p0, z0, 32),
+		z0 = svasrd_x (p0, z0, 32))
+
+/*
+** asrd_32_s32_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.s, p0/m, z1\.s, #32
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_32_s32_x_untied, svint32_t,
+		z1 = svasrd_n_s32_x (p0, z0, 32),
+		z1 = svasrd_x (p0, z0, 32))
diff --git a/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s64.c b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s64.c
new file mode 100644
index 00000000000..40e9c5c09a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s64.c
@@ -0,0 +1,178 @@
+/* { dg-do compile } */
+/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */
+
+#include "test_sve_acle.h"
+
+/*
+** asrd_1_s64_m_tied1:
+**	asrd	z0\.d, p0/m, z0\.d, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s64_m_tied1, svint64_t,
+		z0 = svasrd_n_s64_m (p0, z0, 1),
+		z0 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_1_s64_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.d, p0/m, z1\.d, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s64_m_untied, svint64_t,
+		z1 = svasrd_n_s64_m (p0, z0, 1),
+		z1 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_2_s64_m_tied1:
+**	asrd	z0\.d, p0/m, z0\.d, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s64_m_tied1, svint64_t,
+		z0 = svasrd_n_s64_m (p0, z0, 2),
+		z0 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_2_s64_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.d, p0/m, z1\.d, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s64_m_untied, svint64_t,
+		z1 = svasrd_n_s64_m (p0, z0, 2),
+		z1 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_64_s64_m_tied1:
+**	asrd	z0\.d, p0/m, z0\.d, #64
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_64_s64_m_tied1, svint64_t,
+		z0 = svasrd_n_s64_m (p0, z0, 64),
+		z0 = svasrd_m (p0, z0, 64))
+
+/*
+** asrd_64_s64_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.d, p0/m, z1\.d, #64
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_64_s64_m_untied, svint64_t,
+		z1 = svasrd_n_s64_m (p0, z0, 64),
+		z1 = svasrd_m (p0, z0, 64))
+
+/*
+** asrd_1_s64_z_tied1:
+**	movprfx	z0\.d, p0/z, z0\.d
+**	asrd	z0\.d, p0/m, z0\.d, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s64_z_tied1, svint64_t,
+		z0 = svasrd_n_s64_z (p0, z0, 1),
+		z0 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_1_s64_z_untied:
+**	movprfx	z1\.d, p0/z, z0\.d
+**	asrd	z1\.d, p0/m, z1\.d, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s64_z_untied, svint64_t,
+		z1 = svasrd_n_s64_z (p0, z0, 1),
+		z1 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_2_s64_z_tied1:
+**	movprfx	z0\.d, p0/z, z0\.d
+**	asrd	z0\.d, p0/m, z0\.d, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s64_z_tied1, svint64_t,
+		z0 = svasrd_n_s64_z (p0, z0, 2),
+		z0 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_2_s64_z_untied:
+**	movprfx	z1\.d, p0/z, z0\.d
+**	asrd	z1\.d, p0/m, z1\.d, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s64_z_untied, svint64_t,
+		z1 = svasrd_n_s64_z (p0, z0, 2),
+		z1 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_64_s64_z_tied1:
+**	movprfx	z0\.d, p0/z, z0\.d
+**	asrd	z0\.d, p0/m, z0\.d, #64
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_64_s64_z_tied1, svint64_t,
+		z0 = svasrd_n_s64_z (p0, z0, 64),
+		z0 = svasrd_z (p0, z0, 64))
+
+/*
+** asrd_64_s64_z_untied:
+**	movprfx	z1\.d, p0/z, z0\.d
+**	asrd	z1\.d, p0/m, z1\.d, #64
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_64_s64_z_untied, svint64_t,
+		z1 = svasrd_n_s64_z (p0, z0, 64),
+		z1 = svasrd_z (p0, z0, 64))
+
+/*
+** asrd_1_s64_x_tied1:
+**	asrd	z0\.d, p0/m, z0\.d, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s64_x_tied1, svint64_t,
+		z0 = svasrd_n_s64_x (p0, z0, 1),
+		z0 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_1_s64_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.d, p0/m, z1\.d, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s64_x_untied, svint64_t,
+		z1 = svasrd_n_s64_x (p0, z0, 1),
+		z1 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_2_s64_x_tied1:
+**	asrd	z0\.d, p0/m, z0\.d, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s64_x_tied1, svint64_t,
+		z0 = svasrd_n_s64_x (p0, z0, 2),
+		z0 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_2_s64_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.d, p0/m, z1\.d, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s64_x_untied, svint64_t,
+		z1 = svasrd_n_s64_x (p0, z0, 2),
+		z1 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_64_s64_x_tied1:
+**	asrd	z0\.d, p0/m, z0\.d, #64
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_64_s64_x_tied1, svint64_t,
+		z0 = svasrd_n_s64_x (p0, z0, 64),
+		z0 = svasrd_x (p0, z0, 64))
+
+/*
+** asrd_64_s64_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.d, p0/m, z1\.d, #64
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_64_s64_x_untied, svint64_t,
+		z1 = svasrd_n_s64_x (p0, z0, 64),
+		z1 = svasrd_x (p0, z0, 64))
diff --git a/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s8.c b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s8.c
new file mode 100644
index 00000000000..ad6e372bde4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve-acle/asm/asrd_s8.c
@@ -0,0 +1,178 @@
+/* { dg-do compile } */
+/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */
+
+#include "test_sve_acle.h"
+
+/*
+** asrd_1_s8_m_tied1:
+**	asrd	z0\.b, p0/m, z0\.b, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s8_m_tied1, svint8_t,
+		z0 = svasrd_n_s8_m (p0, z0, 1),
+		z0 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_1_s8_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.b, p0/m, z1\.b, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s8_m_untied, svint8_t,
+		z1 = svasrd_n_s8_m (p0, z0, 1),
+		z1 = svasrd_m (p0, z0, 1))
+
+/*
+** asrd_2_s8_m_tied1:
+**	asrd	z0\.b, p0/m, z0\.b, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s8_m_tied1, svint8_t,
+		z0 = svasrd_n_s8_m (p0, z0, 2),
+		z0 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_2_s8_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.b, p0/m, z1\.b, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s8_m_untied, svint8_t,
+		z1 = svasrd_n_s8_m (p0, z0, 2),
+		z1 = svasrd_m (p0, z0, 2))
+
+/*
+** asrd_8_s8_m_tied1:
+**	asrd	z0\.b, p0/m, z0\.b, #8
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_8_s8_m_tied1, svint8_t,
+		z0 = svasrd_n_s8_m (p0, z0, 8),
+		z0 = svasrd_m (p0, z0, 8))
+
+/*
+** asrd_8_s8_m_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.b, p0/m, z1\.b, #8
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_8_s8_m_untied, svint8_t,
+		z1 = svasrd_n_s8_m (p0, z0, 8),
+		z1 = svasrd_m (p0, z0, 8))
+
+/*
+** asrd_1_s8_z_tied1:
+**	movprfx	z0\.b, p0/z, z0\.b
+**	asrd	z0\.b, p0/m, z0\.b, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s8_z_tied1, svint8_t,
+		z0 = svasrd_n_s8_z (p0, z0, 1),
+		z0 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_1_s8_z_untied:
+**	movprfx	z1\.b, p0/z, z0\.b
+**	asrd	z1\.b, p0/m, z1\.b, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s8_z_untied, svint8_t,
+		z1 = svasrd_n_s8_z (p0, z0, 1),
+		z1 = svasrd_z (p0, z0, 1))
+
+/*
+** asrd_2_s8_z_tied1:
+**	movprfx	z0\.b, p0/z, z0\.b
+**	asrd	z0\.b, p0/m, z0\.b, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s8_z_tied1, svint8_t,
+		z0 = svasrd_n_s8_z (p0, z0, 2),
+		z0 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_2_s8_z_untied:
+**	movprfx	z1\.b, p0/z, z0\.b
+**	asrd	z1\.b, p0/m, z1\.b, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s8_z_untied, svint8_t,
+		z1 = svasrd_n_s8_z (p0, z0, 2),
+		z1 = svasrd_z (p0, z0, 2))
+
+/*
+** asrd_8_s8_z_tied1:
+**	movprfx	z0\.b, p0/z, z0\.b
+**	asrd	z0\.b, p0/m, z0\.b, #8
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_8_s8_z_tied1, svint8_t,
+		z0 = svasrd_n_s8_z (p0, z0, 8),
+		z0 = svasrd_z (p0, z0, 8))
+
+/*
+** asrd_8_s8_z_untied:
+**	movprfx	z1\.b, p0/z, z0\.b
+**	asrd	z1\.b, p0/m, z1\.b, #8
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_8_s8_z_untied, svint8_t,
+		z1 = svasrd_n_s8_z (p0, z0, 8),
+		z1 = svasrd_z (p0, z0, 8))
+
+/*
+** asrd_1_s8_x_tied1:
+**	asrd	z0\.b, p0/m, z0\.b, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s8_x_tied1, svint8_t,
+		z0 = svasrd_n_s8_x (p0, z0, 1),
+		z0 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_1_s8_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.b, p0/m, z1\.b, #1
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_1_s8_x_untied, svint8_t,
+		z1 = svasrd_n_s8_x (p0, z0, 1),
+		z1 = svasrd_x (p0, z0, 1))
+
+/*
+** asrd_2_s8_x_tied1:
+**	asrd	z0\.b, p0/m, z0\.b, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s8_x_tied1, svint8_t,
+		z0 = svasrd_n_s8_x (p0, z0, 2),
+		z0 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_2_s8_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.b, p0/m, z1\.b, #2
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_2_s8_x_untied, svint8_t,
+		z1 = svasrd_n_s8_x (p0, z0, 2),
+		z1 = svasrd_x (p0, z0, 2))
+
+/*
+** asrd_8_s8_x_tied1:
+**	asrd	z0\.b, p0/m, z0\.b, #8
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_8_s8_x_tied1, svint8_t,
+		z0 = svasrd_n_s8_x (p0, z0, 8),
+		z0 = svasrd_x (p0, z0, 8))
+
+/*
+** asrd_8_s8_x_untied:
+**	movprfx	z1, z0
+**	asrd	z1\.b, p0/m, z1\.b, #8
+**	ret
+*/
+TEST_UNIFORM_Z (asrd_8_s8_x_untied, svint8_t,
+		z1 = svasrd_n_s8_x (p0, z0, 8),
+		z1 = svasrd_x (p0, z0, 8))
diff --git a/gcc/testsuite/gcc.target/aarch64/sve-acle/general-c/asrd_1.c b/gcc/testsuite/gcc.target/aarch64/sve-acle/general-c/asrd_1.c
new file mode 100644
index 00000000000..c2c655ae499
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve-acle/general-c/asrd_1.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c99 -Wall -Wextra" } */
+
+#include <arm_sve.h>
+
+void
+f1 (svbool_t pg, svuint8_t u8, svint8_t s8, svint16_t s16,
+    svint32_t s32, svint64_t s64, int x)
+{
+  const int one = 1;
+  u8 = svasrd_x (pg, u8, 1); /* { dg-error "'svasrd_x' has no form that takes 'svuint8_t' arguments" } */
+  s8 = svasrd_x (pg, s8, x); /* { dg-error "argument 3 of 'svasrd_x' must be an integer constant expression" } */
+  s8 = svasrd_x (pg, s8, one); /* { dg-error "argument 3 of 'svasrd_x' must be an integer constant expression" } */
+  s8 = svasrd_x (pg, s8, 0.4); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_x (pg, s8, 1.0);
+  s8 = svasrd_x (pg, s8, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_x (pg, s8, 1);
+  s8 = svasrd_x (pg, s8, 1 + 1);
+  s8 = svasrd_x (pg, s8, 8);
+  s8 = svasrd_x (pg, s8, 9); /* { dg-error {passing 9 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_x (pg, s8, (1ULL << 62) + 1); /* { dg-error {passing [^ ]* to argument 3 of 'svasrd_x', which expects a value in the range \[1, 8\]} } */
+  s16 = svasrd_x (pg, s16, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 16\]} } */
+  s16 = svasrd_x (pg, s16, 1);
+  s16 = svasrd_x (pg, s16, 16);
+  s16 = svasrd_x (pg, s16, 17); /* { dg-error {passing 17 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 16\]} } */
+  s32 = svasrd_x (pg, s32, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 32\]} } */
+  s32 = svasrd_x (pg, s32, 1);
+  s32 = svasrd_x (pg, s32, 32);
+  s32 = svasrd_x (pg, s32, 33); /* { dg-error {passing 33 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 32\]} } */
+  s64 = svasrd_x (pg, s64, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 64\]} } */
+  s64 = svasrd_x (pg, s64, 1);
+  s64 = svasrd_x (pg, s64, 64);
+  s64 = svasrd_x (pg, s64, 65); /* { dg-error {passing 65 to argument 3 of 'svasrd_x', which expects a value in the range \[1, 64\]} } */
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve-acle/general-c/asrd_2.c b/gcc/testsuite/gcc.target/aarch64/sve-acle/general-c/asrd_2.c
new file mode 100644
index 00000000000..b0738450b1b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve-acle/general-c/asrd_2.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-std=c99 -Wall -Wextra" } */
+
+#include <arm_sve.h>
+
+void
+f1 (svbool_t pg, svint8_t s8, svint16_t s16, svint32_t s32, svint64_t s64,
+    int x)
+{
+  const int one = 1;
+  s8 = svasrd_n_s8_x (pg, s8, x); /* { dg-error "argument 3 of 'svasrd_n_s8_x' must be an integer constant expression" } */
+  s8 = svasrd_n_s8_x (pg, s8, one); /* { dg-error "argument 3 of 'svasrd_n_s8_x' must be an integer constant expression" } */
+  s8 = svasrd_n_s8_x (pg, s8, 0.4); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_n_s8_x (pg, s8, 1.0);
+  s8 = svasrd_n_s8_x (pg, s8, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_n_s8_x (pg, s8, 1);
+  s8 = svasrd_n_s8_x (pg, s8, 1 + 1);
+  s8 = svasrd_n_s8_x (pg, s8, 8);
+  s8 = svasrd_n_s8_x (pg, s8, 9); /* { dg-error {passing 9 to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s8 = svasrd_n_s8_x (pg, s8, (1ULL << 62) + 1); /* { dg-error {passing [^ ]* to argument 3 of 'svasrd_n_s8_x', which expects a value in the range \[1, 8\]} } */
+  s16 = svasrd_n_s16_x (pg, s16, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s16_x', which expects a value in the range \[1, 16\]} } */
+  s16 = svasrd_n_s16_x (pg, s16, 1);
+  s16 = svasrd_n_s16_x (pg, s16, 16);
+  s16 = svasrd_n_s16_x (pg, s16, 17); /* { dg-error {passing 17 to argument 3 of 'svasrd_n_s16_x', which expects a value in the range \[1, 16\]} } */
+  s32 = svasrd_n_s32_x (pg, s32, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s32_x', which expects a value in the range \[1, 32\]} } */
+  s32 = svasrd_n_s32_x (pg, s32, 1);
+  s32 = svasrd_n_s32_x (pg, s32, 32);
+  s32 = svasrd_n_s32_x (pg, s32, 33); /* { dg-error {passing 33 to argument 3 of 'svasrd_n_s32_x', which expects a value in the range \[1, 32\]} } */
+  s64 = svasrd_n_s64_x (pg, s64, 0); /* { dg-error {passing 0 to argument 3 of 'svasrd_n_s64_x', which expects a value in the range \[1, 64\]} } */
+  s64 = svasrd_n_s64_x (pg, s64, 1);
+  s64 = svasrd_n_s64_x (pg, s64, 64);
+  s64 = svasrd_n_s64_x (pg, s64, 65); /* { dg-error {passing 65 to argument 3 of 'svasrd_n_s64_x', which expects a value in the range \[1, 64\]} } */
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]