This patch implements the builtin registration and RTL expansion logic
for APEX custom instructions, connecting the #pragma intrinsic frontend
to the backend machine description patterns.

Format inference analyzes function signatures to determine which encoding
formats (XD, XS, XI, XC) are valid based on parameter count and types.
The arcv_apex_infer_format function checks operand flags against opcode
field widths to compute compatible formats, while arcv_apex_validate_format
verifies user-provided formats match the inferred set.

Builtin expansion follows the standard GCC pattern: arcv_apex_expand_builtin
dispatches to arcv_apex_expand_builtin_direct, which prepares operands via
arcv_apex_prepare_builtin_arg (checking immediate ranges for XS/XI/XC
formats) before calling arcv_apex_expand_builtin_insn to emit the selected
RTL pattern.

Assembly .extInstruction directives are emitted at registration time,
allowing the assembler to recognize and encode APEX mnemonics.

gcc/ChangeLog:

        * config.gcc: Add arcv.o to extra_objs for RISC-V.
        * config/riscv/arcv-builtins.cc (arcv_apex_infer_operand_flags): New
        function to infer APEX_VOID/APEX_NO_SRC0/APEX_NO_SRC1 from signature.
        (arcv_apex_infer_format): New function to compute valid formats from
        operand flags and opcode.
        (arcv_apex_validate_format): New function to check user-provided
        formats against inferred formats.
        (arcv_apex_icode): New function to map format to insn_code.
        (arcv_apex_register_builtin): New function to register APEX builtin
        and emit assembly directive.
        (arcv_apex_immediate_argument_valid_p): New function to check
        immediate ranges for XS/XI/XC formats.
        (arcv_apex_prepare_builtin_arg): New function to prepare and validate
        builtin arguments.
        (arcv_apex_expand_builtin_insn): New function to emit RTL pattern.
        (arcv_apex_expand_builtin_direct): New function to expand direct
        builtin by preparing operands and emitting insn.
        (arcv_apex_expand_builtin): New function to dispatch builtin expansion.
        * config/riscv/riscv-builtins.cc (riscv_builtin_decl): Add
        RISCV_BUILTIN_APEX case with sorry diagnostic for LTO.
        (riscv_gimple_fold_builtin): Add RISCV_BUILTIN_APEX case.
        (riscv_expand_builtin): Likewise.
        * config/riscv/riscv-c.cc (riscv_check_builtin_call): Likewise.
        (riscv_resolve_overloaded_builtin): Likewise.
        * config/riscv/riscv-protos.h (enum riscv_builtin_class): Likewise.
        (arcv_apex_expand_builtin): Declare.
        (arcv_apex_emit_ext_directive): Likewise.
        * config/riscv/riscv-vector-builtins.h: (RVV_EXT_PARTITION_SHIFT):
        Increase from 1 to 2 to match RISCV_BUILTIN_SHIFT.  Update
        partition encoding comment to reflect new bit layout.
        * config/riscv/riscv.h (enum APEX_OPCODE_FIELD_MAX):
        (APEX_FORMAT_MASK): New macro to extract format bits.
        (enum apex_signature_mask): New enum for operand signature bits.
        * config/riscv/t-riscv:  Add build rules for arcv.o.
        * config/riscv/arcv.cc: New file.

Signed-off-by: Luis Silva <[email protected]>
---
 gcc/config.gcc                           |   2 +-
 gcc/config/riscv/arcv-builtins.cc        | 415 +++++++++++++++++++++++
 gcc/config/riscv/arcv.cc                 | 102 ++++++
 gcc/config/riscv/riscv-builtins.cc       |  14 +
 gcc/config/riscv/riscv-c.cc              |   5 +
 gcc/config/riscv/riscv-protos.h          |  13 +-
 gcc/config/riscv/riscv-vector-builtins.h |   8 +-
 gcc/config/riscv/riscv.h                 |  22 ++
 gcc/config/riscv/t-riscv                 |   9 +
 9 files changed, 582 insertions(+), 8 deletions(-)
 create mode 100644 gcc/config/riscv/arcv.cc

diff --git a/gcc/config.gcc b/gcc/config.gcc
index d5283c080cc..c5d3114ed8d 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -568,7 +568,7 @@ riscv*)
        extra_objs="${extra_objs} riscv-v.o riscv-vsetvl.o riscv-vector-costs.o 
riscv-avlprop.o riscv-vect-permconst.o"
        extra_objs="${extra_objs} riscv-vector-builtins.o 
riscv-vector-builtins-shapes.o riscv-vector-builtins-bases.o 
sifive-vector-builtins-bases.o andes-vector-builtins-bases.o"
        extra_objs="${extra_objs} thead.o riscv-target-attr.o riscv-zicfilp.o 
riscv-bclr-lowest-set-bit.o riscv-opt-popretz.o riscv-fusion.o"
-       extra_objs="${extra_objs} arcv-builtins.o"
+       extra_objs="${extra_objs} arcv-builtins.o arcv.o"
        d_target_objs="riscv-d.o"
        extra_headers="riscv_vector.h riscv_crypto.h riscv_bitmanip.h 
riscv_th_vector.h sifive_vector.h andes_vector.h"
        target_gtfiles="$target_gtfiles 
\$(srcdir)/config/riscv/riscv-vector-builtins.cc"
diff --git a/gcc/config/riscv/arcv-builtins.cc 
b/gcc/config/riscv/arcv-builtins.cc
index 8bbfacdb34c..d8a633fe56c 100644
--- a/gcc/config/riscv/arcv-builtins.cc
+++ b/gcc/config/riscv/arcv-builtins.cc
@@ -81,6 +81,7 @@ struct arcv_apex_builtin_description {
 static const int arcv_apex_builtins_limit = 384;
 static struct arcv_apex_builtin_description
 arcv_apex_builtins[arcv_apex_builtins_limit];
+static int arcv_apex_builtin_index = 0;
 
 /* Return the assembly mnemonic for an APEX instruction.
    Appends "i" suffix if immediate_p is true and multiple formats exist.  */
@@ -110,3 +111,417 @@ arcv_apex_format_enabled_p (unsigned int subcode, 
unsigned int insn_format)
   const struct arcv_apex_builtin_description *d = &arcv_apex_builtins[subcode];
   return (d->format_flags & insn_format);
 }
+
+/* Infer APEX operand flags from a built-in function signature.
+   Sets APEX_VOID if return type is void.
+   Sets APEX_NO_SRC0/APEX_NO_SRC1 if parameters are absent.
+   Returns true on success, false on error.  */
+
+static bool
+arcv_apex_infer_operand_flags (unsigned int *insn_format, tree fndecl)
+{
+  tree return_type = TREE_TYPE (TREE_TYPE (fndecl));
+  if (return_type == void_type_node)
+    *insn_format |= APEX_VOID;
+
+  unsigned int nargs = 0;
+  const char *fn_name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+  for (tree arg = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+       arg && TREE_CODE (TREE_VALUE (arg)) != VOID_TYPE;
+       arg = TREE_CHAIN (arg))
+  {
+    if (++nargs > 2)
+    {
+      warning (0, "pragma intrinsic: Associated function can have no more "
+                 "than 2 parameters");
+      return false;
+    }
+
+    /* Only perform size checks on 32-bit architectures.  */
+    if (POINTER_SIZE != 32)
+      continue;
+
+    /* We want to check the size of the value represented by the argument.
+       - If it's a pointer, we check the size of the pointed-to type.
+       - If it's a scalar or aggregate type, we check its own size.  */
+    tree argtype = TREE_VALUE (arg);
+    tree type_to_check = argtype;
+    if (TREE_CODE (argtype) == POINTER_TYPE)
+    {
+      /* Skip size check if return type is void.  */
+      if (return_type == void_type_node)
+       continue;
+
+      type_to_check = TREE_TYPE (argtype);
+    }
+    else
+      type_to_check = argtype;
+
+    /* If TYPE_SIZE_UNIT exists and represents a constant integer value,
+       retrieve its size in bytes as a HOST_WIDE_INT.  */
+    if (! (TYPE_SIZE_UNIT (type_to_check)
+       && tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_to_check))))
+      continue;
+
+    /* If the type's size is greater than 4 bytes, emit an error.
+       This applies to both pointed-to types and scalar types
+       larger than 4 bytes.  */
+    HOST_WIDE_INT bytes = tree_to_uhwi (TYPE_SIZE_UNIT (type_to_check));
+    if (bytes <= 4)
+      continue;
+
+    if (TREE_CODE (argtype) == POINTER_TYPE)
+    {
+      /* Specific error for pointer parameters when return type is not void.  
*/
+      error ("pragma intrinsic: APEX function %qs must return "
+               "void or a scalar type that does not exceed 4 bytes",
+               fn_name);
+    }
+    else
+    {
+      /* General error for large or non-scalar parameter types.  */
+      error ("pragma intrinsic: APEX function %qs contains a parameter "
+               "of a non-scalar type, or one that exceeds 4 bytes",
+               fn_name);
+    }
+      return false;
+  }
+
+  if (nargs < 1)
+    *insn_format |= APEX_NO_SRC0;
+  if (nargs < 2)
+    *insn_format |= APEX_NO_SRC1;
+
+  return true;
+}
+
+/* Infer APEX instruction format when not explicitly specified.
+   Determines the format (XD, XS, XI, XC) based on opcode and operand
+   layout.  */
+
+static unsigned int
+arcv_apex_infer_format (unsigned int insn_format, unsigned int opcode)
+{
+  unsigned int insn_operands = insn_format >> 5;
+
+  /* APEX_XD is always available (most general format).  */
+  insn_format |= APEX_XD;
+
+  /* Add APEX_XS if opcode fits and has two source operands.  */
+  if (opcode <= APEX_OP_MAX_XS
+      && (insn_operands == APEX_VOID_FTYPE_SRC0_SRC1
+         || insn_operands == APEX_DEST_FTYPE_SRC0_SRC1))
+    insn_format |= APEX_XS;
+
+  /* Add APEX_XI if opcode fits and has one source operand.  */
+  if (opcode <= APEX_OP_MAX_XI
+      && (insn_operands == APEX_VOID_FTYPE_SRC0
+         || insn_operands == APEX_DEST_FTYPE_SRC0))
+    insn_format |= APEX_XI;
+
+  /* Add APEX_XC if opcode fits and has dest + two sources.  */
+  if (opcode <= APEX_OP_MAX_XC
+      && insn_operands == APEX_DEST_FTYPE_SRC0_SRC1)
+    insn_format |= APEX_XC;
+
+  return insn_format;
+}
+
+/* Validate APEX instruction format, opcode, and operand count.
+   Checks that the format, opcode, and operands comply with APEX rules.  */
+
+static void
+arcv_apex_validate_format (const char* fn_name, unsigned int insn_format,
+                           unsigned int opcode)
+{
+  gcc_assert (insn_format != APEX_NONE);
+
+  /* Check for duplicate opcode registration.  */
+  for (int i = 0; i < arcv_apex_builtin_index; i++)
+    {
+      if ((arcv_apex_builtins[i].format_flags & APEX_FORMAT_MASK) & insn_format
+         && arcv_apex_builtins[i].opcode == opcode)
+       {
+         error ("pragma intrinsic: this specification defines an opcode "
+      "that duplicates a previous one");
+         return;
+       }
+    }
+
+  bool void_p = (insn_format & APEX_VOID) ? true : false;
+  unsigned int num_args = ((insn_format & APEX_NO_SRC0) ? 0 : 1)
+                       + ((insn_format & APEX_NO_SRC1) ? 0 : 1);
+
+  /* Validate XD format: opcode <= APEX_OP_MAX_XD.  */
+  if (insn_format & APEX_XD)
+    {
+      if (opcode > APEX_OP_MAX_XD)
+       {
+         error ("pragma intrinsic: APEX opcode value %<%d%> must be an "
+                "integer constant in the range 0 to 0xff, inclusive", opcode);
+         return;
+       }
+    }
+
+  /* Validate XS format: opcode <= APEX_OP_MAX_XS, requires 2 arguments.  */
+  if (insn_format & APEX_XS)
+    {
+      if (opcode > APEX_OP_MAX_XS)
+       {
+         error ("pragma intrinsic: APEX opcode value %<%d%> must be an "
+                "integer constant in the range 0 to 0x3f, inclusive", opcode);
+         return;
+       }
+      if (num_args != 2)
+       {
+         error ("pragma intrinsic: APEX function %qs must have 2 scalar "
+                "parameter(s) for the 'XS' format class", fn_name);
+         return;
+       }
+    }
+
+  /* Validate XI format: opcode <= APEX_OP_MAX_XI, requires 1 argument.  */
+  if (insn_format & APEX_XI)
+    {
+      if (opcode > APEX_OP_MAX_XI)
+       {
+         error ("pragma intrinsic: APEX opcode value %<%d%> must be an "
+                "integer constant in the range 0 to 0x1f, inclusive", opcode);
+         return;
+       }
+      if (num_args != 1)
+       {
+         error ("pragma intrinsic: APEX function %qs must have 1 scalar "
+                "parameter(s) for the 'XI' format class", fn_name);
+         return;
+       }
+    }
+
+  /* Validate XC format: opcode <= APEX_OP_MAX_XC, requires 2 arguments
+     and return value.  */
+  if (insn_format & APEX_XC)
+    {
+      if (opcode > APEX_OP_MAX_XC)
+       {
+         error ("pragma intrinsic: APEX opcode value %<%d%> must be an "
+                "integer constant in the range 0 to 0x1f, inclusive", opcode);
+         return;
+       }
+      if (num_args != 2)
+       {
+         error ("pragma intrinsic: APEX function %qs must have 2 scalar "
+                "parameter(s) for the 'XC' format class", fn_name);
+         return;
+       }
+      if (void_p)
+       {
+         error ("pragma intrinsic: APEX function %qs must return the same "
+                "type as the first parameter for the 'XC' format class",
+                fn_name);
+         return;
+       }
+    }
+}
+
+/* Map APEX operand layout to the corresponding instruction code.  */
+
+static enum insn_code
+arcv_apex_icode (unsigned int insn_format)
+{
+  unsigned int insn_operands = insn_format >> 5;
+  bool is_volatile = insn_format & APEX_VOLATILE;
+
+  /* Void return types are always volatile (side effects).  */
+  switch (insn_operands)
+  {
+    case APEX_VOID_FTYPE:
+      return CODE_FOR_riscv_arcv_apex_void_ftype_v;
+
+    case APEX_VOID_FTYPE_SRC0:
+      return CODE_FOR_riscv_arcv_apex_void_ftype_src0_v;
+
+    case APEX_VOID_FTYPE_SRC0_SRC1:
+      return CODE_FOR_riscv_arcv_apex_void_ftype_src0_src1_v;
+
+    case APEX_DEST_FTYPE:
+      return is_volatile ? CODE_FOR_riscv_arcv_apex_dest_ftype_v
+                        : CODE_FOR_riscv_arcv_apex_dest_ftype;
+
+    case APEX_DEST_FTYPE_SRC0:
+      return is_volatile ? CODE_FOR_riscv_arcv_apex_dest_ftype_src0_v
+                        : CODE_FOR_riscv_arcv_apex_dest_ftype_src0;
+
+    case APEX_DEST_FTYPE_SRC0_SRC1:
+      return is_volatile ? CODE_FOR_riscv_arcv_apex_dest_ftype_src0_src1_v
+                        : CODE_FOR_riscv_arcv_apex_dest_ftype_src0_src1;
+
+    default:
+      return CODE_FOR_nothing;
+   }
+}
+
+/* Initialize an APEX built-in from pragma directive.  */
+
+void
+arcv_apex_register_builtin (tree fndecl, const char *fn_name,
+                       const char *mnemonic, unsigned int format_flags,
+                       unsigned int opcode)
+{
+  if (!arcv_apex_infer_operand_flags (&format_flags, fndecl))
+    return;
+
+  if ((format_flags & APEX_FORMAT_MASK) == APEX_NONE)
+    format_flags = arcv_apex_infer_format (format_flags, opcode);
+  arcv_apex_validate_format (fn_name, format_flags, opcode);
+
+  enum insn_code icode = arcv_apex_icode (format_flags);
+  enum riscv_builtin_type builtin_type
+       = (format_flags & APEX_VOID) ? RISCV_BUILTIN_DIRECT_NO_TARGET
+                                    : RISCV_BUILTIN_DIRECT;
+
+  /* Store APEX insn information.  */
+  arcv_apex_builtins[arcv_apex_builtin_index]
+    = { icode, fn_name, xstrdup (mnemonic), opcode, builtin_type,
+       format_flags };
+
+  fndecl->function_decl.built_in_class = BUILT_IN_MD;
+
+  fndecl->function_decl.function_code
+       = (arcv_apex_builtin_index << RISCV_BUILTIN_SHIFT) + RISCV_BUILTIN_APEX;
+
+  arcv_apex_builtin_index++;
+
+  arcv_apex_emit_ext_directive (mnemonic, opcode, format_flags);
+}
+
+/* Validate the immediate argument passed to an APEX intrinsic.
+   Used during builtin expansion.  */
+
+bool
+arcv_apex_immediate_argument_valid_p (unsigned int subcode, tree exp)
+{
+  if (arcv_apex_format_enabled_p (subcode, APEX_XD))
+    return true;
+
+  tree arg;
+  HOST_WIDE_INT val;
+  HOST_WIDE_INT min_val, max_val;
+
+  if (arcv_apex_format_enabled_p (subcode, APEX_XI))
+  {
+    arg = CALL_EXPR_ARG (exp, 0);
+    min_val = -2048;
+    max_val = 2047;
+  }
+  else if (arcv_apex_format_enabled_p (subcode, APEX_XC)
+          || arcv_apex_format_enabled_p (subcode, APEX_XS))
+  {
+    arg = CALL_EXPR_ARG (exp, 1);
+    min_val = arcv_apex_format_enabled_p (subcode, APEX_XS) ? -128 : -2048;
+    max_val = arcv_apex_format_enabled_p (subcode, APEX_XS) ? 127 : 2047;
+  }
+  else
+    return true;
+
+  if (!TREE_CONSTANT (arg) || TREE_CODE (arg) != INTEGER_CST)
+  {
+    error ("argument to %qs must be a constant integer",
+          arcv_apex_builtins[subcode].name);
+    return false;
+  }
+
+  val = tree_to_shwi (arg);
+  if (val < min_val || val > max_val)
+  {
+    error ("argument value %wd is outside the valid range [%wd, %wd]",
+          val, min_val, max_val);
+    return false;
+  }
+
+  return true;
+}
+
+/* Take argument ARGNO from EXP's argument list and convert it into
+   an expand operand.  Store the operand in *OP.  */
+
+static void
+arcv_apex_prepare_builtin_arg (struct expand_operand *op, tree exp,
+                              unsigned argno)
+{
+  tree arg = CALL_EXPR_ARG (exp, argno);
+  create_input_operand (op, expand_normal (arg), TYPE_MODE (TREE_TYPE (arg)));
+}
+
+/* Expand instruction ICODE as part of a built-in function sequence.
+   Use the first NOPS elements of OPS as the instruction's operands.
+   HAS_TARGET_P is true if operand 0 is a target; it is false if the
+   instruction has no target.
+
+   Return the target rtx if HAS_TARGET_P, otherwise return const0_rtx.  */
+
+static rtx
+arcv_apex_expand_builtin_insn (enum insn_code icode, unsigned int n_ops,
+                              struct expand_operand *ops, bool has_target_p)
+{
+  if (!maybe_expand_insn (icode, n_ops, ops))
+    {
+      error ("invalid argument to built-in function");
+      return has_target_p ? gen_reg_rtx (ops[0].mode) : const0_rtx;
+    }
+
+  return has_target_p ? ops[0].value : const0_rtx;
+}
+
+/* Expand a RISCV_BUILTIN_DIRECT or RISCV_BUILTIN_DIRECT_NO_TARGET function
+   for APEX builtins; HAS_TARGET_P says which.  EXP is the CALL_EXPR that
+   calls the function and ICODE is the code of the associated .md pattern.
+   TARGET, if nonnull, suggests a good place to put the result.  */
+
+static rtx
+arcv_apex_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
+                                bool has_target_p, unsigned int subcode)
+{
+  struct expand_operand ops[MAX_RECOG_OPERANDS];
+
+  /* Map any target to operand 0.  */
+  int opno = 0;
+  if (has_target_p)
+    create_output_operand (&ops[opno++], target, TYPE_MODE (TREE_TYPE (exp)));
+
+  /* Create an RTL constant for the APEX subcode.  */
+  rtx const_rtx = GEN_INT (subcode);
+  /* Add the subcode as an additional input operand to the RTL expression.  */
+  create_input_operand (&ops[opno++], const_rtx, SImode);
+  /* Validate the immediate argument passed to the APEX intrinsic.  */
+  if (!arcv_apex_immediate_argument_valid_p (subcode, exp))
+    return const0_rtx;
+
+  /* Map the arguments to the other operands.  */
+  gcc_assert (opno + call_expr_nargs (exp)
+             == insn_data[icode].n_generator_args);
+  for (int argno = 0; argno < call_expr_nargs (exp); argno++)
+    arcv_apex_prepare_builtin_arg (&ops[opno++], exp, argno);
+
+  return arcv_apex_expand_builtin_insn (icode, opno, ops, has_target_p);
+}
+
+/* Expand an APEX builtin.  */
+
+rtx
+arcv_apex_expand_builtin (unsigned int subcode, tree exp, rtx target)
+{
+  const struct arcv_apex_builtin_description *d = &arcv_apex_builtins[subcode];
+
+  switch (d->builtin_type)
+    {
+    case RISCV_BUILTIN_DIRECT:
+      return arcv_apex_expand_builtin_direct (d->icode, target, exp, true,
+                                             subcode);
+
+    case RISCV_BUILTIN_DIRECT_NO_TARGET:
+      return arcv_apex_expand_builtin_direct (d->icode, target, exp, false,
+                                             subcode);
+    }
+
+  gcc_unreachable ();
+}
diff --git a/gcc/config/riscv/arcv.cc b/gcc/config/riscv/arcv.cc
new file mode 100644
index 00000000000..556298e89b4
--- /dev/null
+++ b/gcc/config/riscv/arcv.cc
@@ -0,0 +1,102 @@
+/* Subroutines used for code generation for Synopsys ARC-V processors.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option)
+any later version.
+
+GCC 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/>.  */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "tm.h"
+#include "optabs.h"
+#include "regs.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "diagnostic-core.h"
+#include "stor-layout.h"
+#include "alias.h"
+#include "fold-const.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "explow.h"
+#include "calls.h"
+#include "varasm.h"
+#include "expr.h"
+#include "tm_p.h"
+#include "df.h"
+#include "reload.h"
+#include "sched-int.h"
+#include "tm-constrs.h"
+
+/* Print .extInstruction assembly directives for APEX instruction formats.  */
+
+void
+arcv_apex_emit_ext_directive (const char *mnemonic, int opcode,
+                              unsigned int format_flags)
+{
+  unsigned int operand_sig = format_flags >> 5;
+  bool has_immediate = format_flags & (APEX_XS | APEX_XI | APEX_XC);
+  const char *suffix = (format_flags & APEX_XD) && has_immediate ? "i" : "";
+
+  /* XD: most general, includes operand flags.  */
+  if (format_flags & APEX_XD)
+    {
+      fprintf (asm_out_file, "\t.extInstruction %s,%d,XD", mnemonic, opcode);
+      if (format_flags & APEX_VOID)
+       fputs (",void", asm_out_file);
+      if (format_flags & APEX_NO_SRC0)
+       fputs (",no_src0", asm_out_file);
+      if (format_flags & APEX_NO_SRC1)
+       fputs (",no_src1", asm_out_file);
+      fputc ('\n', asm_out_file);
+    }
+
+  /* XS/XC: dest, src0, src1.  */
+  if ((format_flags & (APEX_XS | APEX_XC))
+      && (operand_sig == APEX_DEST_FTYPE_SRC0_SRC1))
+    {
+      fprintf (asm_out_file, "\t.extInstruction %s%s,%d",
+              mnemonic, suffix, opcode);
+      if (format_flags & APEX_XS)
+       fputs (",XS", asm_out_file);
+      if (format_flags & APEX_XC)
+       fputs (",XC", asm_out_file);
+      fputc ('\n', asm_out_file);
+    }
+
+  /* XI: dest (optional), src0.  */
+  if (format_flags & APEX_XI)
+    {
+      fprintf (asm_out_file, "\t.extInstruction %s%s,%d,XI",
+              mnemonic, suffix, opcode);
+      if (format_flags & APEX_VOID)
+       fputs (",void", asm_out_file);
+      fputc ('\n', asm_out_file);
+    }
+
+  /* XS: void, src0, src1.  */
+  if ((format_flags & APEX_XS) && (operand_sig == APEX_VOID_FTYPE_SRC0_SRC1))
+    fprintf (asm_out_file, "\t.extInstruction %s%s,%d,XS,void\n",
+            mnemonic, suffix, opcode);
+}
diff --git a/gcc/config/riscv/riscv-builtins.cc 
b/gcc/config/riscv/riscv-builtins.cc
index 997e637be91..d58915799f1 100644
--- a/gcc/config/riscv/riscv-builtins.cc
+++ b/gcc/config/riscv/riscv-builtins.cc
@@ -315,6 +315,12 @@ riscv_builtin_decl (unsigned int code, bool initialize_p 
ATTRIBUTE_UNUSED)
 
     case RISCV_BUILTIN_VECTOR:
       return riscv_vector::builtin_decl (subcode, initialize_p);
+
+    case RISCV_BUILTIN_APEX:
+      /* APEX intrinsics are pragma-registered and don't have
+        persistent decls.  */
+      sorry ("APEX intrinsics are not supported with LTO yet");
+      return error_mark_node;
     }
   return error_mark_node;
 }
@@ -393,6 +399,10 @@ riscv_gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case RISCV_BUILTIN_VECTOR:
       new_stmt = riscv_vector::gimple_fold_builtin (subcode, gsi, stmt);
       break;
+
+    case RISCV_BUILTIN_APEX:
+      new_stmt = NULL;
+      break;
     }
 
   if (!new_stmt)
@@ -416,6 +426,10 @@ riscv_expand_builtin (tree exp, rtx target, rtx subtarget 
ATTRIBUTE_UNUSED,
     {
       case RISCV_BUILTIN_VECTOR:
        return riscv_vector::expand_builtin (subcode, exp, target);
+
+      case RISCV_BUILTIN_APEX:
+       return arcv_apex_expand_builtin (subcode, exp, target);
+
       case RISCV_BUILTIN_GENERAL: {
        const struct riscv_builtin_description *d = &riscv_builtins[subcode];
 
diff --git a/gcc/config/riscv/riscv-c.cc b/gcc/config/riscv/riscv-c.cc
index 369aac89c40..1429dbbb080 100644
--- a/gcc/config/riscv/riscv-c.cc
+++ b/gcc/config/riscv/riscv-c.cc
@@ -271,6 +271,9 @@ riscv_check_builtin_call (location_t loc, vec<location_t> 
arg_loc, tree fndecl,
     case RISCV_BUILTIN_VECTOR:
       return riscv_vector::check_builtin_call (loc, arg_loc, subcode,
                                               fndecl, nargs, args);
+
+    case RISCV_BUILTIN_APEX:
+      return true;
     }
   gcc_unreachable ();
 }
@@ -297,6 +300,8 @@ riscv_resolve_overloaded_builtin (location_t loc, tree 
fndecl,
       new_fndecl = riscv_vector::resolve_overloaded_builtin (loc, subcode,
                                                             fndecl, arglist);
       break;
+    case RISCV_BUILTIN_APEX:
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index a601e7392b5..7851fabdfb0 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -815,14 +815,17 @@ rtx get_fp_rounding_coefficient (machine_mode);
 /* We classify builtin types into two classes:
    1. General builtin class which is defined in riscv_builtins.
    2. Vector builtin class which is a special builtin architecture
-      that implement intrinsic short into "pragma".  */
+      that implement intrinsic short into "pragma".
+   3. Apex builtin class which is user-defined custom instruction
+      via "pragma intrinsic()".  */
 enum riscv_builtin_class
 {
   RISCV_BUILTIN_GENERAL,
-  RISCV_BUILTIN_VECTOR
+  RISCV_BUILTIN_VECTOR,
+  RISCV_BUILTIN_APEX
 };
 
-const unsigned int RISCV_BUILTIN_SHIFT = 1;
+const unsigned int RISCV_BUILTIN_SHIFT = 2;
 
 /* Mask that selects the riscv_builtin_class part of a function code.  */
 const unsigned int RISCV_BUILTIN_CLASS = (1 << RISCV_BUILTIN_SHIFT) - 1;
@@ -872,6 +875,10 @@ extern const char *th_asm_output_opcode (FILE 
*asm_out_file, const char *p);
 /* Routines implemented in arcv-builtins.cc.  */
 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);
+
+/* Routines implemented in arcv.cc.  */
+extern void arcv_apex_emit_ext_directive (const char *, int, unsigned int);
 
 #ifdef RTX_CODE
 extern const char*
diff --git a/gcc/config/riscv/riscv-vector-builtins.h 
b/gcc/config/riscv/riscv-vector-builtins.h
index d5fe0cd7a22..1f8e366a508 100644
--- a/gcc/config/riscv/riscv-vector-builtins.h
+++ b/gcc/config/riscv/riscv-vector-builtins.h
@@ -171,12 +171,12 @@ enum rvv_builtin_partition
 };
 
 /* Partition encoding for builtin function codes.
-     Bit 0:       RISCV_BUILTIN_VECTOR (class bit)
-     Bits 1-8:    Partition (rvv_builtin_partition enum)
-     Bits 9+:     Index within partition.
+     Bits 0-1:    RISCV_BUILTIN_VECTOR (class bit)
+     Bits 2-9:    Partition (rvv_builtin_partition enum)
+     Bits 10+:    Index within partition.
      */
 const unsigned int RVV_EXT_PARTITION_BITS = 8;
-const unsigned int RVV_EXT_PARTITION_SHIFT = 1; /* Class Bit.  */
+const unsigned int RVV_EXT_PARTITION_SHIFT = 2; /* Class Bit.  */
 const unsigned int RVV_SUBCODE_SHIFT = RVV_EXT_PARTITION_SHIFT
                                       + RVV_EXT_PARTITION_BITS;
 
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index fc0e11009f8..f9a5bb02337 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -1362,6 +1362,28 @@ enum apex_insn_format {
   APEX_NO_SRC1 = 1 << 7,
 };
 
+/* APEX opcode maximums for each format.  */
+enum APEX_OPCODE_FIELD_MAX
+{
+  APEX_OP_MAX_XD = 0xFF,
+  APEX_OP_MAX_XS = 0x3F,
+  APEX_OP_MAX_XI = 0x1F,
+  APEX_OP_MAX_XC = 0x1F,
+};
+
+/* Mask to extract instruction format from format_flags.  */
+#define APEX_FORMAT_MASK 0xF
+
+/* APEX operand signature masks (3-bit patterns for void/src0/src1).  */
+enum apex_signature_mask  {
+  APEX_VOID_FTYPE          = 0b111,
+  APEX_VOID_FTYPE_SRC0     = 0b101,
+  APEX_VOID_FTYPE_SRC0_SRC1 = 0b001,
+  APEX_DEST_FTYPE          = 0b110,
+  APEX_DEST_FTYPE_SRC0     = 0b100,
+  APEX_DEST_FTYPE_SRC0_SRC1 = 0b000,
+};
+
 #endif /* ! RISCV_APEX_H */
 
 #endif /* ! GCC_RISCV_H */
diff --git a/gcc/config/riscv/t-riscv b/gcc/config/riscv/t-riscv
index 5968140a3c9..436226163c9 100644
--- a/gcc/config/riscv/t-riscv
+++ b/gcc/config/riscv/t-riscv
@@ -192,6 +192,15 @@ arcv-builtins.o: $(srcdir)/config/riscv/arcv-builtins.cc \
        $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
                $(srcdir)/config/riscv/arcv-builtins.cc
 
+arcv.o: $(srcdir)/config/riscv/arcv.cc \
+  $(CONFIG_H) $(SYSTEM_H) coretypes.h $(BACKEND_H) $(TARGET_H) $(RTL_H) \
+  $(TREE_H) memmodel.h $(TM_H) $(OPTABS_H) $(REGS_H) $(EMIT_RTL_H) $(RECOG_H) \
+  $(DIAGNOSTIC_CORE_H) stor-layout.h alias.h fold-const.h output.h insn-attr.h 
\
+  flags.h explow.h calls.h varasm.h $(EXPR_H) $(TM_P_H) df.h reload.h \
+  sched-int.h tm-constrs.h
+       $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+               $(srcdir)/config/riscv/arcv.cc
+
 riscv-zicfilp.o: $(srcdir)/config/riscv/riscv-zicfilp.cc \
     $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(REGS_H) insn-config.h $(RTL_BASE_H) \
     dominance.h cfg.h cfganal.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(RECOG_H) \
-- 
2.34.0

Reply via email to