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