This patch implements the C frontend support for APEX custom instructions
via the #pragma intrinsic directive.  This allows users to bind existing
C function declarations to APEX custom instruction opcodes at compile time.

Syntax:
  #pragma intrinsic(fn_name, "mnemonic", opcode, "format"...)

Where:
- fn_name: Previously declared C function to register as intrinsic
- mnemonic: Assembly instruction name (normalized to lowercase)
- opcode: Instruction opcode (0-255 for XD, 0-63 for XS, 0-31 for XI/XC)
- format: One or more of "XD", "XS", "XI", "XC", or "side_effect"

The pragma parser validates identifier syntax, looks up the function
declaration via lookup_name, extracts format flags from the pragma
arguments, and delegates to arcv_apex_register_builtin for actual
registration.

Format classes:
- XD: Default register-register format (up to 3 registers, 8-bit opcode)
- XS: Two-operand with 8-bit signed immediate (6-bit opcode)
- XI: One-operand with 12-bit signed immediate (5-bit opcode)
- XC: Accumulator format where destination is also source0 (5-bit opcode)
- side_effect: Marks instruction as volatile to prevent optimization

If no format is specified, the compiler infers it from the opcode value
and function signature (see arcv_apex_infer_format in the middle-end
patch).

Example:
  int custom_add (int a, int b);
  #pragma intrinsic(custom_add, "myadd", 42, "XD")

gcc/ChangeLog:

        * config/riscv/riscv-c.cc (arcv_apex_lookup_function): New function
        to lookup function declarations for pragma processing.
        (arcv_apex_valid_identifier_p): New function to validate APEX
        mnemonic identifiers.
        (arcv_apex_pragma_intrinsic): New function to parse #pragma intrinsic
        and register APEX builtins.
        (riscv_register_pragmas): Register #pragma intrinsic.
        * config/riscv/riscv-protos.h (arcv_apex_register_builtin): Declare.
        * doc/extend.texi: New subsection documenting #pragma intrinsic for
        APEX custom instructions.

gcc/testsuite/ChangeLog:

        * g++.target/riscv/apex/apex.exp: New test driver for APEX C++ tests.
        * g++.target/riscv/apex/arcv-apex-test1.C: New test.
        * g++.target/riscv/apex/arcv-apex-test2.C: New test.
        * g++.target/riscv/apex/arcv-apex-test3.C: New test.
        * g++.target/riscv/apex/arcv-apex-test4.C: New test.
        * g++.target/riscv/apex/arcv-apex-test5.C: New test.
        * gcc.target/riscv/apex/apex.exp: New test driver for APEX C tests.
        * gcc.target/riscv/apex/arcv-apex-err1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err10.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err11.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err12.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err13.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err14.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err15.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err16-32.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err16-64.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err17-32.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err17-64.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err18.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err19.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err2.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err20.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err3.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err4.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err5.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err6.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err7.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err8.c: New test.
        * gcc.target/riscv/apex/arcv-apex-err9.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test1.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test10.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test11.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test12.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test13.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test14.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test15.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test16.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test2.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test3.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test4.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test5.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test6.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test7.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test8.c: New test.
        * gcc.target/riscv/apex/arcv-apex-test9.c: New test.

Signed-off-by: Luis Silva <[email protected]>
---
 gcc/config/riscv/riscv-c.cc                   | 219 ++++++++++++++++++
 gcc/config/riscv/riscv-protos.h               |   2 +
 gcc/doc/extend.texi                           |  85 +++++++
 gcc/testsuite/g++.target/riscv/apex/apex.exp  |  30 +++
 .../g++.target/riscv/apex/arcv-apex-test1.C   |  22 ++
 .../g++.target/riscv/apex/arcv-apex-test2.C   |  14 ++
 .../g++.target/riscv/apex/arcv-apex-test3.C   |  21 ++
 .../g++.target/riscv/apex/arcv-apex-test4.C   |  33 +++
 .../g++.target/riscv/apex/arcv-apex-test5.C   |  16 ++
 gcc/testsuite/gcc.target/riscv/apex/apex.exp  |  30 +++
 .../gcc.target/riscv/apex/arcv-apex-err1.c    |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err10.c   |  14 ++
 .../gcc.target/riscv/apex/arcv-apex-err11.c   |  11 +
 .../gcc.target/riscv/apex/arcv-apex-err12.c   |  11 +
 .../gcc.target/riscv/apex/arcv-apex-err13.c   |  13 ++
 .../gcc.target/riscv/apex/arcv-apex-err14.c   |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err15.c   |   9 +
 .../riscv/apex/arcv-apex-err16-32.c           |  14 ++
 .../riscv/apex/arcv-apex-err16-64.c           |  14 ++
 .../riscv/apex/arcv-apex-err17-32.c           |  13 ++
 .../riscv/apex/arcv-apex-err17-64.c           |  13 ++
 .../gcc.target/riscv/apex/arcv-apex-err18.c   |   7 +
 .../gcc.target/riscv/apex/arcv-apex-err19.c   |  13 ++
 .../gcc.target/riscv/apex/arcv-apex-err2.c    |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err20.c   |  16 ++
 .../gcc.target/riscv/apex/arcv-apex-err3.c    |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err4.c    |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err5.c    |   6 +
 .../gcc.target/riscv/apex/arcv-apex-err6.c    |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err7.c    |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err8.c    |   4 +
 .../gcc.target/riscv/apex/arcv-apex-err9.c    |  12 +
 .../gcc.target/riscv/apex/arcv-apex-test1.c   |  22 ++
 .../gcc.target/riscv/apex/arcv-apex-test10.c  |  22 ++
 .../gcc.target/riscv/apex/arcv-apex-test11.c  |  22 ++
 .../gcc.target/riscv/apex/arcv-apex-test12.c  |  21 ++
 .../gcc.target/riscv/apex/arcv-apex-test13.c  |  22 ++
 .../gcc.target/riscv/apex/arcv-apex-test14.c  |  20 ++
 .../gcc.target/riscv/apex/arcv-apex-test15.c  |  20 ++
 .../gcc.target/riscv/apex/arcv-apex-test16.c  |  20 ++
 .../gcc.target/riscv/apex/arcv-apex-test2.c   |  16 ++
 .../gcc.target/riscv/apex/arcv-apex-test3.c   |  15 ++
 .../gcc.target/riscv/apex/arcv-apex-test4.c   |  39 ++++
 .../gcc.target/riscv/apex/arcv-apex-test5.c   |  63 +++++
 .../gcc.target/riscv/apex/arcv-apex-test6.c   |  63 +++++
 .../gcc.target/riscv/apex/arcv-apex-test7.c   |  17 ++
 .../gcc.target/riscv/apex/arcv-apex-test8.c   |  17 ++
 .../gcc.target/riscv/apex/arcv-apex-test9.c   |  20 ++
 48 files changed, 1089 insertions(+)
 create mode 100644 gcc/testsuite/g++.target/riscv/apex/apex.exp
 create mode 100644 gcc/testsuite/g++.target/riscv/apex/arcv-apex-test1.C
 create mode 100644 gcc/testsuite/g++.target/riscv/apex/arcv-apex-test2.C
 create mode 100644 gcc/testsuite/g++.target/riscv/apex/arcv-apex-test3.C
 create mode 100644 gcc/testsuite/g++.target/riscv/apex/arcv-apex-test4.C
 create mode 100644 gcc/testsuite/g++.target/riscv/apex/arcv-apex-test5.C
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/apex.exp
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err10.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err11.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err12.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err13.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err14.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err15.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-32.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-64.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-32.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-64.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err18.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err19.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err20.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err6.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err7.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err8.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err9.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test10.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test11.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test12.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test13.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test14.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test15.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test16.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test6.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test7.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test8.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test9.c

diff --git a/gcc/config/riscv/riscv-c.cc b/gcc/config/riscv/riscv-c.cc
index 1429dbbb080..0a58e0b2f58 100644
--- a/gcc/config/riscv/riscv-c.cc
+++ b/gcc/config/riscv/riscv-c.cc
@@ -31,9 +31,227 @@ along with GCC; see the file COPYING3.  If not see
 #include "target.h"
 #include "tm_p.h"
 #include "riscv-subset.h"
+#include "stringpool.h" /* Used for "get_identifier ()"  */
 
 #define builtin_define(TXT) cpp_define (pfile, TXT)
 
+/* Look up the user-defined function declaration by name.
+
+   Given a function name as a string, this function returns the corresponding
+   tree node for its declaration, if it exists.  If the function is not 
declared
+   in the current scope, an error is reported.  */
+
+tree
+arcv_apex_lookup_function (const char *fn_name)
+{
+  /* Convert the raw string to an interned IDENTIFIER_NODE.  */
+  tree id = get_identifier (fn_name);
+
+  /* Try the current scope (and outer scopes) for a matching declaration.  */
+  tree fndecl = lookup_name (id);
+
+  /* Verify that we really found a function.  */
+  if (fndecl == NULL_TREE || TREE_CODE (fndecl) != FUNCTION_DECL)
+  {
+    error_at (input_location, "%qs is not declared as a function", fn_name);
+    return NULL_TREE;
+  }
+
+  return fndecl;
+}
+
+/* Return true if S is a valid APEX-intrinsic identifier.
+   Rules:
+     - The identifier must begin with an ASCII letter (A–Z or a–z) or
+     an underscore ('_').
+     - All remaining characters must be ASCII letters, digits (0–9) or
+     underscores ('_').
+     - Other symbols are not allowed.
+   Examples of valid identifiers:    "add", "_bar", "Mul3", "op123".
+   Examples of invalid identifiers:  "1foo", "baz.qux", "foo%".  */
+
+static bool
+arcv_apex_valid_identifier_p (const char *s)
+{
+  if (!s || !s[0])
+    return false;
+
+  if (!(ISALPHA (s[0]) || s[0] == '_'))
+    return false;
+
+  for (const char *p = s + 1; *p; ++p)
+    if (!ISALNUM (*p) && *p != '_')
+      return false;
+
+  return true;
+}
+
+/* Parses and handles `#pragma intrinsic` for APEX custom instructions.
+
+   Syntax:
+       #pragma intrinsic(fn_name, "mnemonic", opcode, "format"...)
+
+   Arguments:
+   - fn_name: C function identifier to register as an intrinsic builtin
+   - mnemonic: Assembly instruction name (e.g., "add", "mul")
+   - opcode: Instruction opcode (0-255 for XD, 0-63 for XS, 0-31 for XI/XC)
+   - format: One or more format specifiers:
+       - "XD": Default format (register-register, up to 3 operands)
+       - "XS": Two-operand with 8-bit signed immediate
+       - "XI": One-operand with immediate
+       - "XC": Accumulator format (rd is also src0)
+       - "side_effect": Mark as volatile (prevents optimization)  */
+
+static void
+arcv_apex_pragma_intrinsic (cpp_reader *)
+{
+
+  enum cpp_ttype token;
+  tree x;
+
+  /* Parse open Parenthesis '('  */
+  if (pragma_lex (&x) != CPP_OPEN_PAREN)
+  {
+    error ("missing %<(%< after %<#pragma intrinsic%<");
+    return;
+  }
+
+  /* Parse the function identifier to be marked as intrinsic.  */
+  if (pragma_lex (&x) != CPP_NAME)
+  {
+    warning (0, "expected identifier in '#pragma intrinsic' - ignoring");
+    return;
+  }
+  const char *fn_name = IDENTIFIER_POINTER (x);
+
+  /* Note: We intentionally do not validate the presence of commas strictly.
+     If a comma is missing after the function identifier or after the
+     instruction name, parsing will continue, but the following token
+     will not match the expected type (e.g., string or number), and we’ll
+     always end up reporting a missing or invalid attribute.
+
+     Because of this, explicitly diagnosing missing commas would be redundant
+     and would only clutter the error output.  This behavior is intentional.  
*/
+  pragma_lex (&x);
+
+  /* Parse the mnemonic string, e.g., "add", "mul".  */
+  token = pragma_lex (&x);
+  const char *mnemonic_raw = "";
+  if (token == CPP_STRING)
+    mnemonic_raw = TREE_STRING_POINTER (x);
+  else if (token == CPP_NAME)
+    mnemonic_raw = IDENTIFIER_POINTER (x);
+
+  /* If the mnemonic is empty or absent, report an error.  */
+  if (mnemonic_raw[0] == '\0')
+    error ("pragma intrinsic: APEX attribute 'name' is missing");
+  /* Reject mnemonics that do not follow APEX identifier rules.  */
+  else if (!arcv_apex_valid_identifier_p (mnemonic_raw))
+    error ("pragma intrinsic: APEX name %qs is not lexically valid",
+           mnemonic_raw);
+
+  /* Convert mnemonic to lowercase to normalize it for the assembler.  */
+  char *mnemonic = xstrdup (mnemonic_raw);
+  for (char *p = mnemonic; *p; p++)
+    *p = TOLOWER (*p);
+
+  /* See note above regarding comma handling.  */
+  pragma_lex (&x);
+
+  /* Parse the opcode value (must be an integer).  */
+  if (pragma_lex (&x) != CPP_NUMBER)
+  {
+    error ("pragma intrinsic: APEX attribute 'opcode' is missing");
+    free (mnemonic);
+    return;
+  }
+  unsigned HOST_WIDE_INT opcode = TREE_INT_CST_LOW (x);
+
+  /* Start with no formats selected.  If none are explicitly provided,
+     formats will be determined later at "arcv_resolve_insn_format ()".  */
+  unsigned int format_flags = APEX_NONE;
+
+  const char *attribute = NULL;
+
+  /* Parse zero or more instruction format specifiers.  */
+  while (1)
+  {
+    token = pragma_lex (&x);
+
+    /* Break if end of argument list reached.  */
+    if (token == CPP_CLOSE_PAREN)
+      break;
+
+    /* Expect comma before each format string.  */
+    if (token != CPP_COMMA)
+    {
+      error ("pragma intrinsic: expected %<,%> or %<)%>");
+      return;
+    }
+
+    token = pragma_lex (&x);
+
+    switch (token)
+    {
+      case CPP_STRING:
+       attribute = TREE_STRING_POINTER (x);
+       break;
+      case CPP_NAME:
+       attribute = IDENTIFIER_POINTER (x);
+       break;
+      default:
+       error ("pragma intrinsic: expected attribute");
+       return;
+    }
+
+    /* On first valid format specifier, override the default (NONE).  */
+    if (strcmp (attribute, "XD") == 0)
+      format_flags |= APEX_XD;
+    else if (strcmp (attribute, "XS") == 0)
+      format_flags |= APEX_XS;
+    else if (strcmp (attribute, "XI") == 0)
+      format_flags |= APEX_XI;
+    else if (strcmp (attribute, "XC") == 0)
+      format_flags |= APEX_XC;
+    else if (strcmp (attribute, "side_effect") == 0)
+      format_flags |= APEX_VOLATILE;
+    else
+    {
+      error ("pragma intrinsic: APEX attribute %qs is not recognized",
+            attribute);
+      free (mnemonic);
+      return;
+    }
+
+  }
+
+  /* Check for incompatible APEX instruction format combinations.
+     The XI format cannot be combined with XD, XS, or XC.  */
+  if ((format_flags & APEX_XI)
+      && (format_flags & (APEX_XD | APEX_XS | APEX_XC)))
+  {
+    error ("pragma intrinsic: APEX formats 'XI' and '%s' are not compatible",
+          attribute);
+    free (mnemonic);
+    return;
+  }
+
+  /* Lookup the user-defined function declaration of the APEX intrinsic.  */
+  tree fndecl = arcv_apex_lookup_function (fn_name);
+
+  /* If lookup failed, clean up and return early.  */
+  if (fndecl == NULL_TREE)
+  {
+    free (mnemonic);
+    return;
+  }
+
+  /* Register the specified function as an APEX intrinsic.  */
+  arcv_apex_register_builtin (fndecl, fn_name, mnemonic, format_flags, opcode);
+
+  free (mnemonic);
+}
+
 static int
 riscv_ext_version_value (unsigned major, unsigned minor)
 {
@@ -322,4 +540,5 @@ riscv_register_pragmas (void)
   targetm.check_builtin_call = riscv_check_builtin_call;
   targetm.target_option.pragma_parse = riscv_pragma_target_parse;
   c_register_pragma ("riscv", "intrinsic", riscv_pragma_intrinsic);
+  c_register_pragma_with_expansion (0, "intrinsic", 
arcv_apex_pragma_intrinsic);
 }
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 7851fabdfb0..8d0a46a8f45 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -876,6 +876,8 @@ extern const char *th_asm_output_opcode (FILE 
*asm_out_file, const char *p);
 extern const char* arcv_apex_asm_mnemonic (rtx, bool);
 extern bool arcv_apex_format_enabled_p (unsigned int, unsigned int);
 extern rtx arcv_apex_expand_builtin (unsigned int, tree, rtx);
+extern void arcv_apex_register_builtin (tree, const char *, const char *,
+                                       unsigned int, unsigned int);
 
 /* Routines implemented in arcv.cc.  */
 extern void arcv_apex_emit_ext_directive (const char *, int, unsigned int);
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e14d033267d..d322c65696c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10084,6 +10084,7 @@ option is specified.  @xref{OpenMP}, and @ref{OpenACC}.
 * ARM Pragmas::
 * LoongArch Pragmas::
 * PRU Pragmas::
+* RISC-V Pragmas::
 * RS/6000 and PowerPC Pragmas::
 * S/390 Pragmas::
 * Darwin Pragmas::
@@ -10166,6 +10167,90 @@ entry.  For example:
 
 @end table
 
+@node RISC-V Pragmas
+@subsection RISC-V Pragmas
+
+@table @code
+
+@cindex pragma, intrinsic
+@item #pragma intrinsic (@var{fn}, "@var{mnemonic}", @var{opcode}, 
"@var{format}"@dots{})
+Register a previously declared C function @var{fn} as an APEX custom
+instruction intrinsic.  This binds the function to a specific assembly
+mnemonic and opcode at compile time, so that calls to @var{fn} emit the
+corresponding custom instruction rather than a normal function call.
+
+The arguments are:
+
+@table @var
+@item fn
+The name of a C function already declared in the current scope.  Its
+return type and parameter types determine the operand signature of the
+generated instruction: @code{void} return means no destination register;
+each @code{int}/@code{long}/@code{float}/@code{double} parameter maps to
+a source register or immediate operand.
+
+@item mnemonic
+A string giving the assembly instruction name, e.g.@: @code{"mac"} or
+@code{"crc32"}.  It is normalized to lowercase.
+
+@item opcode
+An integer opcode.  The valid range depends on the instruction format:
+0--255 for @code{XD}, 0--63 for @code{XS}, and 0--31 for @code{XI}
+and @code{XC}.
+
+@item format
+One or more format specifiers (each a separate string argument following
+a comma):
+
+@table @code
+@item "XD"
+Default register-register format (up to 3 operands, 8-bit opcode).
+
+@item "XS"
+Short-immediate format: two register operands plus an 8-bit signed
+immediate (6-bit opcode).
+
+@item "XI"
+Immediate format: one register operand plus an immediate (5-bit opcode).
+Cannot be combined with @code{XD}, @code{XS}, or @code{XC}.
+
+@item "XC"
+Accumulator format: the destination register is also used as the first
+source operand (5-bit opcode).
+
+@item "side_effect"
+Mark the instruction as having side effects.  This prevents the compiler
+from optimizing away or reordering calls to the intrinsic.
+@end table
+
+When no format specifier is given, the compiler infers the format from
+the function's signature and opcode range.  Multiple formats may be
+specified (except @code{XI}, which is exclusive); the compiler selects
+the appropriate encoding based on the operand types at each call site.
+@end table
+
+For example:
+
+@smallexample
+int mac (int, int);
+#pragma intrinsic(mac, "mac", 1, "XD", "XC", "side_effect")
+@end smallexample
+
+@noindent
+This registers @code{mac} as an intrinsic with mnemonic @code{mac},
+opcode 1, supporting both the XD and XC instruction formats, and
+marked as having side effects.  The function takes two parameters
+(source operands) and returns an @code{int} (destination register).
+
+The compiler emits an assembler @code{.extInstruction} directive for each
+registered intrinsic so that the assembler can encode the custom
+instruction.  APEX intrinsics are supported through LTO: intrinsic
+metadata is serialized into a dedicated LTO section and re-registered
+during link-time optimization, with cross-translation-unit conflicts
+(e.g.@: same opcode with different mnemonics) diagnosed as errors.
+
+@end table
+
 @node RS/6000 and PowerPC Pragmas
 @subsection RS/6000 and PowerPC Pragmas
 
diff --git a/gcc/testsuite/g++.target/riscv/apex/apex.exp 
b/gcc/testsuite/g++.target/riscv/apex/apex.exp
new file mode 100644
index 00000000000..2076d362710
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/apex/apex.exp
@@ -0,0 +1,30 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite for RISC-V APEX custom instructions (C++).
+
+# Exit immediately if this isn't a RISC-V target.
+if ![istarget riscv*-*-*] then {
+  return
+}
+
+# Load support libraries.
+load_lib g++-dg.exp
+
+# Run regular single-file tests.
+dg-init
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] "" ""
+dg-finish
diff --git a/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test1.C 
b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test1.C
new file mode 100644
index 00000000000..4b63308a13e
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test1.C
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+
+extern int _foo (int, int);
+#pragma intrinsic (_foo, "FOO", 15)
+
+extern int bar (int);
+#pragma intrinsic (bar, "barop", 14)
+
+int
+main (void)
+{
+  return _foo (1, 2) + bar (10);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 4 } } */
+/* { dg-final { scan-assembler-times ".extInstruction foo,15,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fooi,15,XS,XC" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction barop,14,XD,no_src1" 1 } 
} */
+/* { dg-final { scan-assembler-times ".extInstruction baropi,14,XI" 1 } } */
+
+/* { dg-final { scan-assembler "baropi\ta\[0-9\],10" } } */
+/* { dg-final { scan-assembler "fooi\ta\[0-9\],a\[0-9\],2" } } */
diff --git a/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test2.C 
b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test2.C
new file mode 100644
index 00000000000..19e2bb243d5
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test2.C
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+
+extern "C" int cfoo (int, int);
+#pragma intrinsic (cfoo, "cfop", 20)
+
+int
+main (void)
+{
+  return cfoo (3, 4);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction cfop,20,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction cfopi,20,XS,XC" 1 } } */
diff --git a/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test3.C 
b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test3.C
new file mode 100644
index 00000000000..481e6277312
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test3.C
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+extern int custom_add (int, int);
+#pragma intrinsic (custom_add, "myadd", 42, "XD")
+
+template <typename T>
+T apply (T a, T b)
+{
+  return custom_add (a, b);
+}
+
+int
+main (void)
+{
+  return apply (10, 20);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction myadd,42,XD" 1 } } */
+/* { dg-final { scan-assembler "myadd\ta\[0-9\],a\[0-9\],a\[0-9\]" } } */
diff --git a/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test4.C 
b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test4.C
new file mode 100644
index 00000000000..879b52af4ca
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test4.C
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+
+void void_func ();
+void void_unary (int);
+void void_binary (int, int);
+
+int func ();
+int unary (int);
+int binary (int, int);
+
+#pragma intrinsic (void_func, "VOID_FUNC", 1, "XD")
+#pragma intrinsic (void_unary, "VOID_UNARY", 2, "XD")
+#pragma intrinsic (void_binary, "VOID_BINARY", 3, "XD")
+#pragma intrinsic (func, "FUNC", 4, "XD")
+#pragma intrinsic (unary, "UNARY", 5, "XD")
+#pragma intrinsic (binary, "BINARY", 6, "XD")
+
+int
+test (void)
+{
+  void_func ();
+  void_unary (1);
+  void_binary (1, 2);
+  return func () + unary (3) + binary (4, 5);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 6 } } */
+/* { dg-final { scan-assembler-times ".extInstruction void_func,1,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction void_unary,2,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction void_binary,3,XD" 1 } } 
*/
+/* { dg-final { scan-assembler-times ".extInstruction func,4,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction unary,5,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction binary,6,XD" 1 } } */
diff --git a/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test5.C 
b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test5.C
new file mode 100644
index 00000000000..47cdbd3ff6a
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/apex/arcv-apex-test5.C
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+extern void side_op (int, int);
+#pragma intrinsic (side_op, "sideop", 10, "XD", "side_effect")
+
+void
+test (int a, int b)
+{
+  side_op (a, b);
+  side_op (a, b);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction sideop,10,XD" 1 } } */
+/* { dg-final { scan-assembler-times "sideop\ta\[0-9\],a\[0-9\]" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/apex.exp 
b/gcc/testsuite/gcc.target/riscv/apex/apex.exp
new file mode 100644
index 00000000000..07a23718671
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/apex.exp
@@ -0,0 +1,30 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite for RISC-V APEX custom instructions.
+
+# Exit immediately if this isn't a RISC-V target.
+if ![istarget riscv*-*-*] then {
+  return
+}
+
+# Load support libraries.
+load_lib gcc-dg.exp
+
+# Run tests.
+dg-init
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] "" ""
+dg-finish
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err1.c
new file mode 100644
index 00000000000..54a77376a27
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err1.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+int foo (int,int);
+#pragma intrinsic (foo,FOP,10,Key=>"bar") /* { dg-error "pragma intrinsic: 
APEX attribute 'Key' is not recognized" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err10.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err10.c
new file mode 100644
index 00000000000..c597acd75a1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err10.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+
+int foo (int);
+#pragma intrinsic (foo, FOP, 10, "XI")
+
+int
+main (void)
+{
+  int a;
+  a =  foo (-2049); /* { dg-error "argument value -2049 is outside the valid 
range \\\[-2048, 2047\\\]" } */
+  a += foo (2048); /* { dg-error "argument value 2048 is outside the valid 
range \\\[-2048, 2047\\\]" } */
+  a += foo (1000000); /* { dg-error "argument value 1000000 is outside the 
valid range \\\[-2048, 2047\\\]" } */
+  return a;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err11.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err11.c
new file mode 100644
index 00000000000..1c0af196c9b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err11.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+int foo (int, int);
+#pragma intrinsic (foo, FOP, 10, "XC")
+
+int main() {
+  int x = 12;
+  int y = foo (x, 100000); /* { dg-error "argument value 100000 is outside the 
valid range \\\[-2048, 2047\\\]" } */
+  return foo (y, x); /* { dg-error "argument to 'foo' must be a constant 
integer" } */
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err12.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err12.c
new file mode 100644
index 00000000000..8b28f8b8191
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err12.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+int foo (int, int);
+#pragma intrinsic (foo, FOP, 10, "XS")
+
+int main() {
+  int x = 12;
+  int y = foo (x, 100000); /* { dg-error "argument value 100000 is outside the 
valid range \\\[-128, 127\\\]" } */
+  return foo (y, x); /* { dg-error "argument to 'foo' must be a constant 
integer" } */
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err13.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err13.c
new file mode 100644
index 00000000000..337239b1d22
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err13.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+void foo (int, int);
+int bar (int);
+
+#pragma intrinsic (foo, FOP, 10, "XS")
+#pragma intrinsic (bar, BAR, 11, "XI")
+
+int main() {
+  foo (12, 100000); /* { dg-error "argument value 100000 is outside the valid 
range \\\[-128, 127\\\]" } */
+  return bar (100000); /* { dg-error "argument value 100000 is outside the 
valid range \\\[-2048, 2047\\\]" } */
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err14.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err14.c
new file mode 100644
index 00000000000..a61926fc28f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err14.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+void foo (int, int);
+#pragma intrinsic (foo, FOP, 10, "XC") /* { dg-error "pragma intrinsic: APEX 
function 'foo' must return the same type as the first parameter for the 'XC' 
format class" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err15.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err15.c
new file mode 100644
index 00000000000..ac711dc5bc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err15.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+int foo (int);
+int bar0 ();
+int bar3 (int,int,int);
+
+#pragma intrinsic (foo, FOP, 10, "XS") /* { dg-error "pragma intrinsic: APEX 
function 'foo' must have 2 scalar parameter\\(s\\) for the 'XS' format class" } 
*/
+#pragma intrinsic (bar0, bar0, 11, "XS") /* { dg-error "pragma intrinsic: APEX 
function 'bar0' must have 2 scalar parameter\\(s\\) for the 'XS' format class" 
} */
+#pragma intrinsic (bar3, bar3, 12, "XS") /* { dg-warning "pragma intrinsic: 
Associated function can have no more than 2 parameters" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-32.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-32.c
new file mode 100644
index 00000000000..d844e71a7f9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-32.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=ilp32d -march=rv32gc" }*/
+
+struct S {int a,b,c,d; };
+
+long long foo (struct S *a, int b);
+#pragma intrinsic (foo,"FOP",10, XD,XS) /* { dg-error "pragma intrinsic: APEX 
function 'foo' must return void or a scalar type that does not exceed 4 bytes" 
} */
+
+int
+main (void)
+{
+  struct S s = {1,2,3,4};
+  return foo (&s, 100);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-64.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-64.c
new file mode 100644
index 00000000000..7a5ec58ca28
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err16-64.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -march=rv64gc" }*/
+
+struct S {int a,b,c,d; };
+
+long long foo (struct S *a, int b);
+#pragma intrinsic (foo,"FOP",10, XD,XS)
+
+int
+main (void)
+{
+  struct S s = {1,2,3,4};
+  return foo (&s, 100);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-32.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-32.c
new file mode 100644
index 00000000000..027a3f81a75
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-32.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=ilp32d -march=rv32gc" }*/
+
+struct S {int a,b,c,d; };
+
+int foo (long long a, int b);
+#pragma intrinsic (foo,"FOP",10, XD,XS) /* { dg-error "pragma intrinsic: APEX 
function 'foo' contains a parameter of a non-scalar type, or one that exceeds 4 
bytes" } */
+
+int
+main (void)
+{
+  return foo (0xdeadbeefcafebabeULL, 100);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-64.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-64.c
new file mode 100644
index 00000000000..367c30802b1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err17-64.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -march=rv64gc" } */
+
+struct S {int a,b,c,d; };
+
+int foo (long long a, int b);
+#pragma intrinsic (foo,"FOP",10, XD,XS)
+
+int
+main (void)
+{
+  return foo (0xdeadbeefcafebabeULL, 100);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err18.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err18.c
new file mode 100644
index 00000000000..5d1319cb8ab
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err18.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+
+int foo (int, int);
+int bar (int, int);
+
+#pragma intrinsic (foo, "FOO", 10, XD, XS)
+#pragma intrinsic (bar, "BAR", 10, XS) /* { dg-error "pragma intrinsic: this 
specification defines an opcode that duplicates a previous one" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err19.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err19.c
new file mode 100644
index 00000000000..68e6d44193c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err19.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+
+int instname_XD (int, int);
+#pragma intrinsic (instname_XD,"instname_XD", 256,"XD") /* { dg-error "pragma 
intrinsic: APEX opcode value '256' must be an integer constant in the range 0 
to 0xff, inclusive" } */
+
+int instname_XS (int, int);
+#pragma intrinsic ( instname_XS,"instname_XS", 64,"XS") /* { dg-error "pragma 
intrinsic: APEX opcode value '64' must be an integer constant in the range 0 to 
0x3f, inclusive" } */
+
+int instname_XC (int, int);
+#pragma intrinsic (instname_XC,"instname_XC", 32,"XC") /* { dg-error "pragma 
intrinsic: APEX opcode value '32' must be an integer constant in the range 0 to 
0x1f, inclusive" } */
+
+int instname_XI (int);
+#pragma intrinsic (instname_XI,"instname_XI", 32,"XI") /* { dg-error "pragma 
intrinsic: APEX opcode value '32' must be an integer constant in the range 0 to 
0x1f, inclusive" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err2.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err2.c
new file mode 100644
index 00000000000..35b12046670
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err2.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+int foo (int,int);
+#pragma intrinsic (foo,FOP,10,"XU") /* { dg-error "pragma intrinsic: APEX 
attribute 'XU' is not recognized" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err20.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err20.c
new file mode 100644
index 00000000000..f0aa821b3be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err20.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+
+/* sub-opcodes reuse.  */
+int foo (int,int);
+#pragma intrinsic (foo, FOP, 1, "XD")
+#pragma intrinsic (foo, FOP, 1, "XD")  /* { dg-error "pragma intrinsic: this 
specification defines an opcode that duplicates a previous one" } */
+
+#pragma intrinsic (foo, FOP, 2, "XS")
+#pragma intrinsic (foo, FOP, 2, "XS")  /* { dg-error "pragma intrinsic: this 
specification defines an opcode that duplicates a previous one" } */
+
+#pragma intrinsic (foo, FOP, 4, "XC")
+#pragma intrinsic (foo, FOP, 4, "XC")  /* { dg-error "pragma intrinsic: this 
specification defines an opcode that duplicates a previous one" } */
+
+int bar (int);
+#pragma intrinsic (bar, BAR, 8, "XI")
+#pragma intrinsic (bar, BAR, 8, "XI")  /* { dg-error "pragma intrinsic: this 
specification defines an opcode that duplicates a previous one" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err3.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err3.c
new file mode 100644
index 00000000000..0a12ff0e9fb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err3.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+int foo (int,int);
+#pragma intrinsic (foo,FOP,10,"XC",opcode=>)  /* { dg-error "pragma intrinsic: 
APEX attribute 'opcode' is not recognized" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err4.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err4.c
new file mode 100644
index 00000000000..660a5ed10e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err4.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+int foo (int);
+#pragma intrinsic (foo,FOP,10,"XI","XC")  /* { dg-error "pragma intrinsic: 
APEX formats 'XI' and 'XC' are not compatible" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err5.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err5.c
new file mode 100644
index 00000000000..d1f677a03a5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err5.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+
+int foo (int);
+#pragma intrinsic (foo)
+/* { dg-error "pragma intrinsic: APEX attribute 'name' is missing" "" { target 
*-*-* } 4 } */
+/* { dg-error "pragma intrinsic: APEX attribute 'opcode' is missing" "" { 
target *-*-* } 4 } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err6.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err6.c
new file mode 100644
index 00000000000..4a5a64c129b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err6.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+int foo (int);
+#pragma intrinsic (foo, "%FOP", 10, "XD")  /* { dg-error "pragma intrinsic: 
APEX name '%FOP' is not lexically valid" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err7.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err7.c
new file mode 100644
index 00000000000..55b871f4f21
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err7.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+int foo (int);
+#pragma intrinsic (foo, FOP, 10000, "XD") /* { dg-error "pragma intrinsic: 
APEX opcode value '10000' must be an integer constant in the range 0 to 0xff, 
inclusive" } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err8.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err8.c
new file mode 100644
index 00000000000..68f6324f27f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err8.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+int foo (int, int);
+#pragma intrinsic (foo, FOP, 10, "XI")  /* { dg-error "pragma intrinsic: APEX 
function 'foo' must have 1 scalar parameter\\(s\\) for the 'XI' format class" } 
*/
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err9.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err9.c
new file mode 100644
index 00000000000..520f50d4804
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-err9.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+int foo (int);
+#pragma intrinsic (foo, FOP, 10, "XI")
+
+int
+main (void)
+{
+  int x = 12;
+  return foo (x); /* { dg-error "argument to 'foo' must be a constant integer" 
} */
+}
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test1.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test1.c
new file mode 100644
index 00000000000..557f3fbf91e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+
+extern int _foo (int, int);
+#pragma intrinsic (_foo, "FOO", 15)
+
+extern int bar (int);
+#pragma intrinsic (bar, "barop", 14)
+
+int
+main (void)
+{
+  return _foo(1,2) + bar(10);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 4 } } */
+/* { dg-final { scan-assembler-times ".extInstruction foo,15,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fooi,15,XS,XC" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction barop,14,XD,no_src1" 1 } 
} */
+/* { dg-final { scan-assembler-times ".extInstruction baropi,14,XI" 1 } } */
+
+/* { dg-final { scan-assembler "baropi\ta\[0-9\],10" } } */
+/* { dg-final { scan-assembler "fooi\ta\[0-9\],a\[0-9\],2" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test10.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test10.c
new file mode 100644
index 00000000000..279a39b0f39
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test10.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O" }*/
+
+#define OPCODE 123
+
+int instname_XD (int, int);
+#pragma intrinsic (instname_XD, "__instname_XD", OPCODE, "XD")
+
+int
+main (void)
+{
+  int a = 123;
+  int xd_large = instname_XD (a, 0XFFFF);
+  int xd_255   = instname_XD (a, 255);
+  int xd_1     = instname_XD (a, 1);
+  return xd_1 + xd_255 + xd_large;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction __instname_xd,123,XD" 1 
} } */
+
+/* { dg-final { scan-assembler-times 
"__instname_xd\ta\[0-5\],a\[0-5\],a\[0-5\]" 3 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test11.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test11.c
new file mode 100644
index 00000000000..8d6983775e0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test11.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O" }*/
+
+#define OPCODE 0
+
+int instname_XS (int, int);
+#pragma intrinsic (instname_XS, "__instname_XS", OPCODE, "XS")
+
+int
+main (void)
+{
+  int a = 123;
+  int xs_max = instname_XS (a, 127);
+  int xs_min = instname_XS (a, -128);
+  return xs_min + xs_max;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction __instname_xs,0,XS" 1 } 
} */
+
+/* { dg-final { scan-assembler-times "__instname_xs\ta\[0-5\],a\[0-5\],127" 1 
} } */
+/* { dg-final { scan-assembler-times "__instname_xs\ta\[0-5\],a\[0-5\],-128" 1 
} } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test12.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test12.c
new file mode 100644
index 00000000000..538527c7a23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test12.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O" }*/
+
+#define OPCODE 0x7
+
+int instname_XI (int);
+#pragma intrinsic (instname_XI, "__instname_XI", OPCODE, "XI")
+
+int
+main (void)
+{
+  int xi_max = instname_XI (2047);
+  int xi_min = instname_XI (-2048);
+  return xi_min + xi_max;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction __instname_xi,7,XI" 1 } 
} */
+
+/* { dg-final { scan-assembler-times "__instname_xi\ta\[0-9\],2047" 1 } } */
+/* { dg-final { scan-assembler-times "__instname_xi\ta\[0-9\],-2048" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test13.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test13.c
new file mode 100644
index 00000000000..fa11f7e2170
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test13.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O" }*/
+
+#define OPCODE 15
+
+int instname_XC (int, int);
+#pragma intrinsic (instname_XC, "__instname_XC", OPCODE, "XC")
+
+int
+main (void)
+{
+  int a = 0x0BADCAFE;
+  int xc_max = instname_XC (a,2047);
+  int xc_min = instname_XC (a,-2048);
+  return xc_min + xc_max;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction __instname_xc,15,XC" 1 } 
} */
+
+/* { dg-final { scan-assembler-times "__instname_xc\ta\[0-9\],a\[0-9\],2047" 1 
} } */
+/* { dg-final { scan-assembler-times "__instname_xc\ta\[0-9\],a\[0-9\],-2048" 
1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test14.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test14.c
new file mode 100644
index 00000000000..004dc9f8f35
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test14.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+extern int op_xd_xc (int, int);
+#pragma intrinsic (op_xd_xc, "myop_xd_xc", 10, "XD", "XC")
+
+int
+test (int a, int b)
+{
+  int r1 = op_xd_xc (a, b);
+  int r2 = op_xd_xc (a, 100);
+  return r1 + r2;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction myop_xd_xc,10,XD" 1 } } 
*/
+/* { dg-final { scan-assembler-times ".extInstruction myop_xd_xci,10,XC" 1 } } 
*/
+
+/* { dg-final { scan-assembler "myop_xd_xc\ta\[0-9\],a\[0-9\],a\[0-9\]" } } */
+/* { dg-final { scan-assembler "myop_xd_xci\ta\[0-9\],a\[0-9\],100" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test15.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test15.c
new file mode 100644
index 00000000000..1e985b3281b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test15.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+extern int op_xd_xs (int, int);
+#pragma intrinsic (op_xd_xs, "myop_xd_xs", 20, "XD", "XS")
+
+int
+test (int a, int b)
+{
+  int r1 = op_xd_xs (a, b);
+  int r2 = op_xd_xs (a, 42);
+  return r1 + r2;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction myop_xd_xs,20,XD" 1 } } 
*/
+/* { dg-final { scan-assembler-times ".extInstruction myop_xd_xsi,20,XS" 1 } } 
*/
+
+/* { dg-final { scan-assembler "myop_xd_xs\ta\[0-9\],a\[0-9\],a\[0-9\]" } } */
+/* { dg-final { scan-assembler "myop_xd_xsi\ta\[0-9\],a\[0-9\],42" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test16.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test16.c
new file mode 100644
index 00000000000..8edbc4a322e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test16.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+extern int op_all (int, int);
+#pragma intrinsic (op_all, "myop_all", 5, "XD", "XS", "XC")
+
+int
+test (int a, int b)
+{
+  int r1 = op_all (a, b);
+  int r2 = op_all (a, 7);
+  return r1 + r2;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction myop_all,5,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction myop_alli,5,XS,XC" 1 } } 
*/
+
+/* { dg-final { scan-assembler "myop_all\ta\[0-9\],a\[0-9\],a\[0-9\]" } } */
+/* { dg-final { scan-assembler "myop_alli\ta\[0-9\],a\[0-9\],7" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test2.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test2.c
new file mode 100644
index 00000000000..e5a8d397396
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+
+extern int foo (int, int);
+#pragma intrinsic (foo, "FOP", 15)
+
+int
+main (int argc, const char **argv)
+{
+  return foo (argc,-2048);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fop,15,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fopi,15,XS,XC" 1 } } */
+
+/* { dg-final { scan-assembler-times "fopi\ta\[0-9\],a\[0-9\],-2048" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test3.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test3.c
new file mode 100644
index 00000000000..5e4fcea3e82
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test3.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+
+extern int _foo (int, int);
+#pragma intrinsic (_foo, "FOO", 100)
+
+int
+test (int a, int b)
+{
+  return _foo (a,b);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction foo,100,XD" 1 } } */
+
+/* { dg-final { scan-assembler "foo\ta\[0-9\],a\[0-9\],a\[0-9\]" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test4.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test4.c
new file mode 100644
index 00000000000..0b950963d93
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test4.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+
+extern int foo2 (int, int);
+extern int foo1 (int);
+extern int foo0 ();
+extern void vfoo2 (int, int);
+extern void vfoo1 (int);
+extern void vfoo0 ();
+
+#pragma intrinsic (foo2, "FOP2", 102, "XD")
+#pragma intrinsic (foo1, "FOP1", 101, "XD")
+#pragma intrinsic (foo0, "FOP0", 100, "XD")
+#pragma intrinsic (vfoo2, "VOP2", 202, "XD")
+#pragma intrinsic (vfoo1, "VOP1", 201, "XD")
+#pragma intrinsic (vfoo0, "VOP0", 200, "XD")
+
+int
+test (void)
+{
+  vfoo2 (1,2);
+  vfoo1 (3);
+  vfoo0 ();
+  return foo2 (1,2) + foo1 (3) + foo0 ();
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 6 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fop2,102,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fop1,101,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fop0,100,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction vop2,202,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction vop1,201,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction vop0,200,XD" 1 } } */
+
+/* { dg-final { scan-assembler "fop2\ta\[0-9\],a\[0-9\],a\[0-9\]" } } */
+/* { dg-final { scan-assembler "fop1\ta\[0-9\],a\[0-9\]" } } */
+/* { dg-final { scan-assembler "fop0\ta\[0-9\]" } } */
+/* { dg-final { scan-assembler "vop2\ta\[0-9\],a\[0-9\]" } } */
+/* { dg-final { scan-assembler "vop1\ta\[0-9\]" } } */
+/* { dg-final { scan-assembler "vop0" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test5.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test5.c
new file mode 100644
index 00000000000..f1179114d4c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test5.c
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+
+void void_func ();
+void void_unary (int);
+void void_binary (int, int);
+
+int func ();
+int unary (int);
+int binary (int, int);
+
+#pragma intrinsic (void_func, "VOID_FUNC", 1)
+#pragma intrinsic (void_unary, "VOID_UNARY", 2)
+#pragma intrinsic (void_binary, "VOID_BINARY", 3)
+#pragma intrinsic (func, "FUNC", 4)
+#pragma intrinsic (unary, "UNARY", 5)
+#pragma intrinsic (binary, "BINARY", 6)
+
+int
+foo (int a, int b, int c)
+{
+  void_func ();
+
+  void_unary (a);
+  void_unary (12);
+  void_unary (-2048);
+  void_unary (10000);
+
+  void_binary (a, b);
+  void_binary (a, 12);
+  void_binary (b, 2047);
+  void_binary (a, 10000);
+
+  return func () + unary (a) + unary (12) + unary (-2048) + unary (10000)
+     + binary (a, b) + binary (a, 12) + binary (c, 1000) + binary (a, 100000);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 10 } } */
+/* { dg-final { scan-assembler-times ".extInstruction 
void_func,1,XD,void,no_src0,no_src1" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction 
void_unary,2,XD,void,no_src1" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction void_unaryi,2,XI,void" 1 
} } */
+/* { dg-final { scan-assembler-times ".extInstruction void_binary,3,XD,void" 1 
} } */
+/* { dg-final { scan-assembler-times ".extInstruction void_binaryi,3,XS,void" 
1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction 
func,4,XD,no_src0,no_src1" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction unary,5,XD,no_src1" 1 } 
} */
+/* { dg-final { scan-assembler-times ".extInstruction unaryi,5,XI" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction binary,6,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction binaryi,6,XS,XC" 1 } } */
+
+/* { dg-final { scan-assembler-times "\tvoid_func" 1 } } */
+/* { dg-final { scan-assembler-times "\tvoid_unaryi\t(12|0xc)" 1 } } */
+/* { dg-final { scan-assembler-times "\tvoid_unaryi\t(-2048|-0x800)" 1 } } */
+/* { dg-final { scan-assembler-times "void_unary\t(a|t)\[0-5\]" 2 } } */
+/* { dg-final { scan-assembler-times "void_binaryi\t(a|t)\[0-5\],(12|0xc)" 1 } 
} */
+/* { dg-final { scan-assembler-times "void_binary\t(a|t)\[0-5\],(a|t)\[0-5\]" 
3 } } */
+/* { dg-final { scan-assembler-times "func\t(a|t)\[0-5\]" 1 } } */
+/* { dg-final { scan-assembler-times "unaryi\t(a|t)\[0-5\],(12|0xc)" 1 } } */
+/* { dg-final { scan-assembler-times "unaryi\t(a|t)\[0-5\],(-2048|-0x800)" 1 } 
} */
+/* { dg-final { scan-assembler-times "unary\t(a|t)\[0-5\],(a|t)\[0-5\]" 2 } } 
*/
+/* { dg-final { scan-assembler-times 
"binaryi\t(a|t)\[0-5\],(a|t)\[0-5\],(12|0xc)" 1 } } */
+
+/* { dg-final { scan-assembler-times 
"binaryi\t(a|t)\[0-5\],(a|t)\[0-5\],(1000|0x3e8)" 1 } } */
+
+/* { dg-final { scan-assembler-times 
"binary\t(a|t)\[0-5\],(a|t)\[0-5\],(a|t)\[0-5\]" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test6.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test6.c
new file mode 100644
index 00000000000..4d2ae25e817
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test6.c
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -march=rv64gc" }*/
+
+#include <stdint.h>
+void void_func ();
+void void_unary (int64_t);
+void void_binary (int64_t, int64_t);
+
+int64_t func ();
+int64_t unary (int64_t);
+int64_t binary (int64_t, int64_t);
+
+#pragma intrinsic (void_func, "VOID_FUNC", 1)
+#pragma intrinsic (void_unary, "VOID_UNARY", 2)
+#pragma intrinsic (void_binary, "VOID_BINARY", 3)
+#pragma intrinsic (func, "FUNC", 4)
+#pragma intrinsic (unary, "UNARY", 5)
+#pragma intrinsic (binary, "BINARY", 6)
+
+int64_t
+foo (int64_t a, int64_t b, int64_t c)
+{
+  void_func ();
+
+  void_unary (a);
+  void_unary (12);
+  void_unary (-2048);
+  void_unary (10000);
+
+  void_binary (a, b);
+  void_binary (a, 12);
+  void_binary (b, 2047);
+  void_binary (a, 10000);
+
+  return func () + unary (a) + unary (12) + unary (-2048) + unary (10000)
+     + binary (a, b) + binary (a, 12) + binary (c, 1000) + binary (a, 100000);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 10 } } */
+/* { dg-final { scan-assembler-times ".extInstruction 
void_func,1,XD,void,no_src0,no_src1" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction 
void_unary,2,XD,void,no_src1" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction void_unaryi,2,XI,void" 1 
} } */
+/* { dg-final { scan-assembler-times ".extInstruction void_binary,3,XD,void" 1 
} } */
+/* { dg-final { scan-assembler-times ".extInstruction void_binaryi,3,XS,void" 
1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction 
func,4,XD,no_src0,no_src1" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction unary,5,XD,no_src1" 1 } 
} */
+/* { dg-final { scan-assembler-times ".extInstruction unaryi,5,XI" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction binary,6,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction binaryi,6,XS,XC" 1 } } */
+
+/* { dg-final { scan-assembler-times "\tvoid_func" 1 } } */
+/* { dg-final { scan-assembler-times "\tvoid_unaryi\t(12|0xc)" 1 } } */
+/* { dg-final { scan-assembler-times "\tvoid_unaryi\t(-2048|-0x800)" 1 } } */
+/* { dg-final { scan-assembler-times "void_unary\t(a|t)\[0-5\]" 2 } } */
+/* { dg-final { scan-assembler-times "void_binaryi\t(a|t)\[0-5\],(12|0xc)" 1 } 
} */
+/* { dg-final { scan-assembler-times "void_binary\t(a|t)\[0-5\],(a|t)\[0-5\]" 
3 } } */
+/* { dg-final { scan-assembler-times "func\t(a|t)\[0-5\]" 1 } } */
+/* { dg-final { scan-assembler-times "unaryi\t(a|t)\[0-5\],(12|0xc)" 1 } } */
+/* { dg-final { scan-assembler-times "unaryi\t(a|t)\[0-5\],(-2048|-0x800)" 1 } 
} */
+/* { dg-final { scan-assembler-times "unary\t(a|t)\[0-5\],(a|t)\[0-5\]" 2 } } 
*/
+/* { dg-final { scan-assembler-times 
"binaryi\t(a|t)\[0-5\],(a|t)\[0-5\],(12|0xc)" 1 } } */
+/* { dg-final { scan-assembler-times 
"binaryi\t(a|t)\[0-5\],(a|t)\[0-5\],(1000|0x3e8)" 1 } } */
+/* { dg-final { scan-assembler-times 
"binary\t(a|t)\[0-5\],(a|t)\[0-5\],(a|t)\[0-5\]" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test7.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test7.c
new file mode 100644
index 00000000000..d3bb044facb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test7.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O" }*/
+
+extern int foo (int, int);
+#pragma intrinsic (foo, "fop", 10)
+
+int
+test (int a, int b)
+{
+  return foo (a, b) + foo (a, b) + foo (a, b);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fop,10,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fopi,10,XS,XC" 1 } } */
+
+/* { dg-final { scan-assembler-times "fop\ta\[0-5\],a\[0-5\],a\[0-5\]" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test8.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test8.c
new file mode 100644
index 00000000000..c96ae2b6d26
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test8.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O" }*/
+
+extern int foo (int, int);
+#pragma intrinsic (foo, "fop", 10, side_effect)
+
+int
+test (int a, int b)
+{
+  return foo (a, b) + foo (a, b) + foo (a, b);
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fop,10,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fopi,10,XS,XC" 1 } } */
+
+/* { dg-final { scan-assembler-times "fop\ta\[0-5\],a\[0-5\],a\[0-5\]" 3 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test9.c 
b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test9.c
new file mode 100644
index 00000000000..0869f225284
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/apex/arcv-apex-test9.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O" }*/
+
+extern int foo (int, int);
+#pragma intrinsic (foo, "fop", 10)
+
+int
+test (int n)
+{
+  int sum = 0;
+  for (unsigned i = 0; i < n; ++i)
+    sum += foo(n, n); /* should be pulled out of the loop.  */
+  return sum;
+}
+
+/* { dg-final { scan-assembler-times ".extInstruction" 2 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fop,10,XD" 1 } } */
+/* { dg-final { scan-assembler-times ".extInstruction fopi,10,XS,XC" 1 } } */
+
+/* { dg-final { scan-assembler-times "fop\ta\[0-5\],a\[0-5\],a\[0-5\]" 1 } } */
-- 
2.34.0

Reply via email to