/* kvx-dis.c -- Kalray MPPA generic disassembler.
  Copyright (C) 2009-2024 Free Software Foundation, Inc.
  Contributed by Kalray SA.

  This file is part of the GNU opcodes library.

  This library 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.

  It 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 this program; see the file COPYING3. If not,
  see <http://www.gnu.org/licenses/>.  */

#define STATIC_TABLE
#define DEFINE_TABLE

#include "sysdep.h"
#include "disassemble.h"
#include "libiberty.h"
#include "opintl.h"
#include <assert.h>
#include "elf-bfd.h"
#include "kvx-dis.h"

#include "elf/kvx.h"
#include "opcode/kvx.h"

/* Steering values for the kvx VLIW architecture.  */

typedef enum
{
 Steering_BCU,
 Steering_LSU,
 Steering_MAU,
 Steering_ALU,
 Steering__
} enum_Steering;
typedef uint8_t Steering;

/* BundleIssue enumeration.  */

typedef enum
{
 BundleIssue_BCU,
 BundleIssue_TCA,
 BundleIssue_ALU0,
 BundleIssue_ALU1,
 BundleIssue_MAU,
 BundleIssue_LSU,
 BundleIssue__,
} enum_BundleIssue;
typedef uint8_t BundleIssue;

/* An IMMX syllable is associated with the BundleIssue Extension_BundleIssue[extension].  */
static const BundleIssue Extension_BundleIssue[] = {
 BundleIssue_ALU0,
 BundleIssue_ALU1,
 BundleIssue_MAU,
 BundleIssue_LSU
};

static inline int
kvx_steering (uint32_t x)
{
 return (((x) & 0x60000000) >> 29);
}

static inline int
kvx_extension (uint32_t x)
{
 return (((x) & 0x18000000) >> 27);
}

static inline int
kvx_has_parallel_bit (uint32_t x)
{
 return (((x) & 0x80000000) == 0x80000000);
}

static inline int
kvx_is_tca_opcode (uint32_t x)
{
 unsigned major = ((x) >> 24) & 0x1F;
 return (major > 1) && (major < 8);
}

static inline int
kvx_is_nop_opcode (uint32_t x)
{
 return ((x) << 1) == 0xFFFFFFFE;
}

/* A raw instruction.  */

struct insn_s
{
 uint32_t syllables[KVXMAXSYLLABLES];
 int len;
};
typedef struct insn_s insn_t;


static uint32_t bundle_words[KVXMAXBUNDLEWORDS];

static insn_t bundle_insn[KVXMAXBUNDLEISSUE];

/* A re-interpreted instruction.  */

struct instr_s
{
 int valid;
 int opcode;
 int immx[2];
 int immx_valid[2];
 int immx_count;
 int nb_syllables;
};

/* Option for "pretty printing", ie, not the usual little endian objdump output.  */
static int opt_pretty = 0;
/* Option for not emiting a new line between all bundles.  */
static int opt_compact_assembly = 0;

void
parse_kvx_dis_option (const char *option)
{
 /* Try to match options that are simple flags.  */
 if (startswith (option, "pretty"))
   {
     opt_pretty = 1;
     return;
   }

 if (startswith (option, "compact-assembly"))
   {
     opt_compact_assembly = 1;
     return;
   }

 if (startswith (option, "no-compact-assembly"))
   {
     opt_compact_assembly = 0;
     return;
   }

 /* Invalid option.  */
 opcodes_error_handler (_("unrecognised disassembler option: %s"), option);
}

static void
parse_kvx_dis_options (const char *options)
{
 const char *option_end;

 if (options == NULL)
   return;

 while (*options != '\0')
   {
     /* Skip empty options.  */
     if (*options == ',')
       {
         options++;
         continue;
       }

     /* We know that *options is neither NUL or a comma.  */
     option_end = options + 1;
     while (*option_end != ',' && *option_end != '\0')
       option_end++;

     parse_kvx_dis_option (options);

     /* Go on to the next one.  If option_end points to a comma, it
        will be skipped above.  */
     options = option_end;
   }
}

struct kvx_dis_env
{
 int kvx_arch_size;
 struct kvxopc *opc_table;
 struct kvx_Register *kvx_registers;
 const char ***kvx_modifiers;
 int *kvx_dec_registers;
 int *kvx_regfiles;
 unsigned int kvx_max_dec_registers;
 int initialized_p;
};

static struct kvx_dis_env env = {
 .kvx_arch_size = 0,
 .opc_table = NULL,
 .kvx_registers = NULL,
 .kvx_modifiers = NULL,
 .kvx_dec_registers = NULL,
 .kvx_regfiles = NULL,
 .initialized_p = 0,
 .kvx_max_dec_registers = 0
};

static void
kvx_dis_init (struct disassemble_info *info)
{
 env.kvx_arch_size = 32;
 switch (info->mach)
   {
   case bfd_mach_kv3_1_64:
     env.kvx_arch_size = 64;
     /* fallthrough */
   case bfd_mach_kv3_1_usr:
   case bfd_mach_kv3_1:
   default:
     env.opc_table = kvx_kv3_v1_optab;
     env.kvx_regfiles = kvx_kv3_v1_regfiles;
     env.kvx_registers = kvx_kv3_v1_registers;
     env.kvx_modifiers = kvx_kv3_v1_modifiers;
     env.kvx_dec_registers = kvx_kv3_v1_dec_registers;
     break;
   case bfd_mach_kv3_2_64:
     env.kvx_arch_size = 64;
     /* fallthrough */
   case bfd_mach_kv3_2_usr:
   case bfd_mach_kv3_2:
     env.opc_table = kvx_kv3_v2_optab;
     env.kvx_regfiles = kvx_kv3_v2_regfiles;
     env.kvx_registers = kvx_kv3_v2_registers;
     env.kvx_modifiers = kvx_kv3_v2_modifiers;
     env.kvx_dec_registers = kvx_kv3_v2_dec_registers;
     break;
   case bfd_mach_kv4_1_64:
     env.kvx_arch_size = 64;
     /* fallthrough */
   case bfd_mach_kv4_1_usr:
   case bfd_mach_kv4_1:
     env.opc_table = kvx_kv4_v1_optab;
     env.kvx_regfiles = kvx_kv4_v1_regfiles;
     env.kvx_registers = kvx_kv4_v1_registers;
     env.kvx_modifiers = kvx_kv4_v1_modifiers;
     env.kvx_dec_registers = kvx_kv4_v1_dec_registers;
     break;
   }

 env.kvx_max_dec_registers = env.kvx_regfiles[KVX_REGFILE_DEC_REGISTERS];

 if (info->disassembler_options)
   parse_kvx_dis_options (info->disassembler_options);

 env.initialized_p = 1;
}

static bool
kvx_reassemble_bundle (int wordcount, int *_insncount)
{

 /* Debugging flag.  */
 int debug = 0;

 /* Available resources.  */
 int bcu_taken = 0;
 int tca_taken = 0;
 int alu0_taken = 0;
 int alu1_taken = 0;
 int mau_taken = 0;
 int lsu_taken = 0;

 if (debug)
   fprintf (stderr, "kvx_reassemble_bundle: wordcount = %d\n", wordcount);

 if (wordcount > KVXMAXBUNDLEWORDS)
   {
     if (debug)
       fprintf (stderr, "bundle exceeds maximum size\n");
     return false;
   }

 struct instr_s instr[KVXMAXBUNDLEISSUE];
 memset (instr, 0, sizeof (instr));
 assert (KVXMAXBUNDLEISSUE >= BundleIssue__);

 int i;
 unsigned int j;

 for (i = 0; i < wordcount; i++)
   {
     uint32_t syllable = bundle_words[i];
     switch (kvx_steering (syllable))
       {
       case Steering_BCU:
         /* BCU or TCA instruction.  */
         if (i == 0)
           {
             if (kvx_is_tca_opcode (syllable))
               {
                 if (tca_taken)
                   {
                     if (debug)
                       fprintf (stderr, "Too many TCA instructions");
                     return false;
                   }
                 if (debug)
                   fprintf (stderr,
                            "Syllable 0: Set valid on TCA for instr %d with 0x%x\n",
                            BundleIssue_TCA, syllable);
                 instr[BundleIssue_TCA].valid = 1;
                 instr[BundleIssue_TCA].opcode = syllable;
                 instr[BundleIssue_TCA].nb_syllables = 1;
                 tca_taken = 1;
               }
             else
               {
                 if (debug)
                   fprintf (stderr,
                            "Syllable 0: Set valid on BCU for instr %d with 0x%x\n",
                            BundleIssue_BCU, syllable);

                 instr[BundleIssue_BCU].valid = 1;
                 instr[BundleIssue_BCU].opcode = syllable;
                 instr[BundleIssue_BCU].nb_syllables = 1;
                 bcu_taken = 1;
               }
           }
         else
           {
             if (i == 1 && bcu_taken && kvx_is_tca_opcode (syllable))
               {
                 if (tca_taken)
                   {
                     if (debug)
                       fprintf (stderr, "Too many TCA instructions");
                     return false;
                   }
                 if (debug)
                   fprintf (stderr,
                            "Syllable 0: Set valid on TCA for instr %d with 0x%x\n",
                            BundleIssue_TCA, syllable);
                 instr[BundleIssue_TCA].valid = 1;
                 instr[BundleIssue_TCA].opcode = syllable;
                 instr[BundleIssue_TCA].nb_syllables = 1;
                 tca_taken = 1;
               }
             else
               {
                 /* Not first syllable in bundle, IMMX.  */
                 struct instr_s *instr_p =
                   &(instr[Extension_BundleIssue[kvx_extension (syllable)]]);
                 int immx_count = instr_p->immx_count;
                 if (immx_count > 1)
                   {
                     if (debug)
                       fprintf (stderr, "Too many IMMX syllables");
                     return false;
                   }
                 instr_p->immx[immx_count] = syllable;
                 instr_p->immx_valid[immx_count] = 1;
                 instr_p->nb_syllables++;
                 if (debug)
                   fprintf (stderr,
                            "Set IMMX[%d] on instr %d for extension %d @ %d\n",
                            immx_count,
                            Extension_BundleIssue[kvx_extension (syllable)],
                            kvx_extension (syllable), i);
                 instr_p->immx_count = immx_count + 1;
               }
           }
         break;

       case Steering_ALU:
         if (alu0_taken == 0)
           {
             if (debug)
               fprintf (stderr, "Set valid on ALU0 for instr %d with 0x%x\n",
                        BundleIssue_ALU0, syllable);
             instr[BundleIssue_ALU0].valid = 1;
             instr[BundleIssue_ALU0].opcode = syllable;
             instr[BundleIssue_ALU0].nb_syllables = 1;
             alu0_taken = 1;
           }
         else if (alu1_taken == 0)
           {
             if (debug)
               fprintf (stderr, "Set valid on ALU1 for instr %d with 0x%x\n",
                        BundleIssue_ALU1, syllable);
             instr[BundleIssue_ALU1].valid = 1;
             instr[BundleIssue_ALU1].opcode = syllable;
             instr[BundleIssue_ALU1].nb_syllables = 1;
             alu1_taken = 1;
           }
         else if (mau_taken == 0)
           {
             if (debug)
               fprintf (stderr,
                        "Set valid on MAU (ALU) for instr %d with 0x%x\n",
                        BundleIssue_MAU, syllable);
             instr[BundleIssue_MAU].valid = 1;
             instr[BundleIssue_MAU].opcode = syllable;
             instr[BundleIssue_MAU].nb_syllables = 1;
             mau_taken = 1;
           }
         else if (lsu_taken == 0)
           {
             if (debug)
               fprintf (stderr,
                        "Set valid on LSU (ALU) for instr %d with 0x%x\n",
                        BundleIssue_LSU, syllable);
             instr[BundleIssue_LSU].valid = 1;
             instr[BundleIssue_LSU].opcode = syllable;
             instr[BundleIssue_LSU].nb_syllables = 1;
             lsu_taken = 1;
           }
         else if (kvx_is_nop_opcode (syllable))
           {
             if (debug)
               fprintf (stderr, "Ignoring NOP (ALU) syllable\n");
           }
         else
           {
             if (debug)
               fprintf (stderr, "Too many ALU instructions");
             return false;
           }
         break;

       case Steering_MAU:
         if (mau_taken == 1)
           {
             if (debug)
               fprintf (stderr, "Too many MAU instructions");
             return false;
           }
         else
           {
             if (debug)
               fprintf (stderr, "Set valid on MAU for instr %d with 0x%x\n",
                        BundleIssue_MAU, syllable);
             instr[BundleIssue_MAU].valid = 1;
             instr[BundleIssue_MAU].opcode = syllable;
             instr[BundleIssue_MAU].nb_syllables = 1;
             mau_taken = 1;
           }
         break;

       case Steering_LSU:
         if (lsu_taken == 1)
           {
             if (debug)
               fprintf (stderr, "Too many LSU instructions");
             return false;
           }
         else
           {
             if (debug)
               fprintf (stderr, "Set valid on LSU for instr %d with 0x%x\n",
                        BundleIssue_LSU, syllable);
             instr[BundleIssue_LSU].valid = 1;
             instr[BundleIssue_LSU].opcode = syllable;
             instr[BundleIssue_LSU].nb_syllables = 1;
             lsu_taken = 1;
           }
       }
     if (debug)
       fprintf (stderr, "Continue %d < %d?\n", i, wordcount);
   }

 /* Fill bundle_insn and count read syllables.  */
 int instr_idx = 0;
 for (i = 0; i < KVXMAXBUNDLEISSUE; i++)
   {
     if (instr[i].valid == 1)
       {
         int syllable_idx = 0;

         /* First copy opcode.  */
         bundle_insn[instr_idx].syllables[syllable_idx++] = instr[i].opcode;
         bundle_insn[instr_idx].len = 1;

         for (j = 0; j < 2; j++)
           {
             if (instr[i].immx_valid[j])
               {
                 if (debug)
                   fprintf (stderr, "Instr %d valid immx[%d] is valid\n", i,
                            j);
                 bundle_insn[instr_idx].syllables[syllable_idx++] =
                   instr[i].immx[j];
                 bundle_insn[instr_idx].len++;
               }
           }

         if (debug)
           fprintf (stderr,
                    "Instr %d valid, copying in bundle_insn (%d syllables <-> %d)\n",
                    i, bundle_insn[instr_idx].len, instr[i].nb_syllables);
         instr_idx++;
       }
   }

 if (debug)
   fprintf (stderr, "End => %d instructions\n", instr_idx);

 *_insncount = instr_idx;
 return true;
}

struct decoded_insn
{
 /* The entry in the opc_table. */
 struct kvxopc *opc;
 /* The number of operands.  */
 int nb_ops;
 /* The content of an operands.  */
 struct
 {
   enum
   {
     CAT_REGISTER,
     CAT_MODIFIER,
     CAT_IMMEDIATE,
   } type;
   /* The value of the operands.  */
   uint64_t val;
   /* If it is an immediate, its sign.  */
   int sign;
   /* If it is an immediate, is it pc relative.  */
   int pcrel;
   /* The width of the operand.  */
   int width;
   /* If it is a modifier, the modifier category.
      An index in the modifier table.  */
   int mod_idx;
 } operands[KVXMAXOPERANDS];
};

static int
decode_insn (bfd_vma memaddr, insn_t * insn, struct decoded_insn *res)
{

 int found = 0;
 int idx = 0;
 for (struct kvxopc * op = env.opc_table;
      op->as_op && (((char) op->as_op[0]) != 0); op++)
   {
     /* Find the format of this insn.  */
     int opcode_match = 1;

     if (op->wordcount != insn->len)
       continue;

     for (int i = 0; i < op->wordcount; i++)
       if ((op->codewords[i].mask & insn->syllables[i]) !=
           op->codewords[i].opcode)
         opcode_match = 0;

     int encoding_space_flags = env.kvx_arch_size == 32
       ? kvxOPCODE_FLAG_MODE32 : kvxOPCODE_FLAG_MODE64;

     for (int i = 0; i < op->wordcount; i++)
       if (!(op->codewords[i].flags & encoding_space_flags))
         opcode_match = 0;

     if (opcode_match)
       {
         res->opc = op;

         for (int i = 0; op->format[i]; i++)
           {
             struct kvx_bitfield *bf = op->format[i]->bfield;
             int bf_nb = op->format[i]->bitfields;
             int width = op->format[i]->width;
             int type = op->format[i]->type;
             const char *type_name = op->format[i]->tname;
             int flags = op->format[i]->flags;
             int shift = op->format[i]->shift;
             int bias = op->format[i]->bias;
             uint64_t value = 0;

             for (int bf_idx = 0; bf_idx < bf_nb; bf_idx++)
               {
                 int insn_idx = (int) bf[bf_idx].to_offset / 32;
                 int to_offset = bf[bf_idx].to_offset % 32;
                 uint64_t encoded_value =
                   insn->syllables[insn_idx] >> to_offset;
                 encoded_value &= (1LL << bf[bf_idx].size) - 1;
                 value |= encoded_value << bf[bf_idx].from_offset;
               }
             if (flags & kvxSIGNED)
               {
                 uint64_t signbit = 1LL << (width - 1);
                 value = (value ^ signbit) - signbit;
               }
             value = (value << shift) + bias;

#define KVX_PRINT_REG(regfile,value) \
   if(env.kvx_regfiles[regfile]+value < env.kvx_max_dec_registers) { \
       res->operands[idx].val = env.kvx_dec_registers[env.kvx_regfiles[regfile]+value]; \
       res->operands[idx].type = CAT_REGISTER; \
       idx++; \
   } else { \
       res->operands[idx].val = ~0; \
       res->operands[idx].type = CAT_REGISTER; \
       idx++; \
   }

             if (env.opc_table == kvx_kv3_v1_optab)
               {
                 switch (type)
                   {
                   case RegClass_kv3_v1_singleReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_GPR, value)
                     break;
                   case RegClass_kv3_v1_pairedReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_PGR, value)
                     break;
                   case RegClass_kv3_v1_quadReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_QGR, value)
                     break;
                   case RegClass_kv3_v1_systemReg:
                   case RegClass_kv3_v1_aloneReg:
                   case RegClass_kv3_v1_onlyraReg:
                   case RegClass_kv3_v1_onlygetReg:
                   case RegClass_kv3_v1_onlysetReg:
                   case RegClass_kv3_v1_onlyfxReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_SFR, value)
                     break;
                   case RegClass_kv3_v1_coproReg0M4:
                   case RegClass_kv3_v1_coproReg1M4:
                   case RegClass_kv3_v1_coproReg2M4:
                   case RegClass_kv3_v1_coproReg3M4:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XCR, value)
                     break;
                   case RegClass_kv3_v1_blockRegE:
                   case RegClass_kv3_v1_blockRegO:
                   case RegClass_kv3_v1_blockReg0M4:
                   case RegClass_kv3_v1_blockReg1M4:
                   case RegClass_kv3_v1_blockReg2M4:
                   case RegClass_kv3_v1_blockReg3M4:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XBR, value)
                     break;
                   case RegClass_kv3_v1_vectorReg:
                   case RegClass_kv3_v1_vectorRegE:
                   case RegClass_kv3_v1_vectorRegO:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XVR, value)
                     break;
                   case RegClass_kv3_v1_tileReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XTR, value)
                     break;
                   case RegClass_kv3_v1_matrixReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XMR, value)
                     break;
                   case Immediate_kv3_v1_sysnumber:
                   case Immediate_kv3_v1_signed10:
                   case Immediate_kv3_v1_signed16:
                   case Immediate_kv3_v1_signed27:
                   case Immediate_kv3_v1_wrapped32:
                   case Immediate_kv3_v1_signed37:
                   case Immediate_kv3_v1_signed43:
                   case Immediate_kv3_v1_signed54:
                   case Immediate_kv3_v1_wrapped64:
                   case Immediate_kv3_v1_unsigned6:
                     res->operands[idx].val = value;
                     res->operands[idx].sign = flags & kvxSIGNED;
                     res->operands[idx].width = width;
                     res->operands[idx].type = CAT_IMMEDIATE;
                     res->operands[idx].pcrel = 0;
                     idx++;
                     break;
                   case Immediate_kv3_v1_pcrel17:
                   case Immediate_kv3_v1_pcrel27:
                     res->operands[idx].val = value + memaddr;
                     res->operands[idx].sign = flags & kvxSIGNED;
                     res->operands[idx].width = width;
                     res->operands[idx].type = CAT_IMMEDIATE;
                     res->operands[idx].pcrel = 1;
                     idx++;
                     break;
                   case Modifier_kv3_v1_column:
                   case Modifier_kv3_v1_comparison:
                   case Modifier_kv3_v1_doscale:
                   case Modifier_kv3_v1_exunum:
                   case Modifier_kv3_v1_floatcomp:
                   case Modifier_kv3_v1_qindex:
                   case Modifier_kv3_v1_rectify:
                   case Modifier_kv3_v1_rounding:
                   case Modifier_kv3_v1_roundint:
                   case Modifier_kv3_v1_saturate:
                   case Modifier_kv3_v1_scalarcond:
                   case Modifier_kv3_v1_silent:
                   case Modifier_kv3_v1_simplecond:
                   case Modifier_kv3_v1_speculate:
                   case Modifier_kv3_v1_splat32:
                   case Modifier_kv3_v1_variant:
                     {
                       int sz = 0;
                       int mod_idx = type - Modifier_kv3_v1_column;
                       for (sz = 0; env.kvx_modifiers[mod_idx][sz]; ++sz);
                       const char *mod = value < (unsigned) sz
                         ? env.kvx_modifiers[mod_idx][value] : NULL;
                       if (!mod) goto retry;
                       res->operands[idx].val = value;
                       res->operands[idx].type = CAT_MODIFIER;
                       res->operands[idx].mod_idx = mod_idx;
                       idx++;
                     }
                     break;
                   default:
                     fprintf (stderr, "error: unexpected operand type (%s)\n",
                              type_name);
                     exit (-1);
                   };
               }
             else if (env.opc_table == kvx_kv3_v2_optab)
               {
                 switch (type)
                   {
                   case RegClass_kv3_v2_singleReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_GPR, value)
                     break;
                   case RegClass_kv3_v2_pairedReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_PGR, value)
                     break;
                   case RegClass_kv3_v2_quadReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_QGR, value)
                     break;
                   case RegClass_kv3_v2_systemReg:
                   case RegClass_kv3_v2_aloneReg:
                   case RegClass_kv3_v2_onlyraReg:
                   case RegClass_kv3_v2_onlygetReg:
                   case RegClass_kv3_v2_onlysetReg:
                   case RegClass_kv3_v2_onlyfxReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_SFR, value)
                     break;
                   case RegClass_kv3_v2_coproReg:
                   case RegClass_kv3_v2_coproReg0M4:
                   case RegClass_kv3_v2_coproReg1M4:
                   case RegClass_kv3_v2_coproReg2M4:
                   case RegClass_kv3_v2_coproReg3M4:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XCR, value)
                     break;
                   case RegClass_kv3_v2_blockReg:
                   case RegClass_kv3_v2_blockRegE:
                   case RegClass_kv3_v2_blockRegO:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XBR, value)
                     break;
                   case RegClass_kv3_v2_vectorReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XVR, value)
                     break;
                   case RegClass_kv3_v2_tileReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XTR, value)
                     break;
                   case RegClass_kv3_v2_matrixReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XMR, value)
                     break;
                   case RegClass_kv3_v2_buffer2Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X2R, value)
                     break;
                   case RegClass_kv3_v2_buffer4Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X4R, value)
                     break;
                   case RegClass_kv3_v2_buffer8Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X8R, value)
                     break;
                   case RegClass_kv3_v2_buffer16Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X16R, value)
                     break;
                   case RegClass_kv3_v2_buffer32Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X32R, value)
                     break;
                   case RegClass_kv3_v2_buffer64Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X64R, value)
                     break;
                   case Immediate_kv3_v2_brknumber:
                   case Immediate_kv3_v2_sysnumber:
                   case Immediate_kv3_v2_signed10:
                   case Immediate_kv3_v2_signed16:
                   case Immediate_kv3_v2_signed27:
                   case Immediate_kv3_v2_wrapped32:
                   case Immediate_kv3_v2_signed37:
                   case Immediate_kv3_v2_signed43:
                   case Immediate_kv3_v2_signed54:
                   case Immediate_kv3_v2_wrapped64:
                   case Immediate_kv3_v2_unsigned6:
                     res->operands[idx].val = value;
                     res->operands[idx].sign = flags & kvxSIGNED;
                     res->operands[idx].width = width;
                     res->operands[idx].type = CAT_IMMEDIATE;
                     res->operands[idx].pcrel = 0;
                     idx++;
                     break;
                   case Immediate_kv3_v2_pcrel27:
                   case Immediate_kv3_v2_pcrel17:
                     res->operands[idx].val = value + memaddr;
                     res->operands[idx].sign = flags & kvxSIGNED;
                     res->operands[idx].width = width;
                     res->operands[idx].type = CAT_IMMEDIATE;
                     res->operands[idx].pcrel = 1;
                     idx++;
                     break;
                   case Modifier_kv3_v2_accesses:
                   case Modifier_kv3_v2_boolcas:
                   case Modifier_kv3_v2_cachelev:
                   case Modifier_kv3_v2_channel:
                   case Modifier_kv3_v2_coherency:
                   case Modifier_kv3_v2_comparison:
                   case Modifier_kv3_v2_conjugate:
                   case Modifier_kv3_v2_doscale:
                   case Modifier_kv3_v2_exunum:
                   case Modifier_kv3_v2_floatcomp:
                   case Modifier_kv3_v2_hindex:
                   case Modifier_kv3_v2_lsomask:
                   case Modifier_kv3_v2_lsumask:
                   case Modifier_kv3_v2_lsupack:
                   case Modifier_kv3_v2_qindex:
                   case Modifier_kv3_v2_rounding:
                   case Modifier_kv3_v2_scalarcond:
                   case Modifier_kv3_v2_shuffleV:
                   case Modifier_kv3_v2_shuffleX:
                   case Modifier_kv3_v2_silent:
                   case Modifier_kv3_v2_simplecond:
                   case Modifier_kv3_v2_speculate:
                   case Modifier_kv3_v2_splat32:
                   case Modifier_kv3_v2_transpose:
                   case Modifier_kv3_v2_variant:
                     {
                       int sz = 0;
                       int mod_idx = type - Modifier_kv3_v2_accesses;
                       for (sz = 0; env.kvx_modifiers[mod_idx][sz];
                            ++sz);
                       const char *mod = value < (unsigned) sz
                         ? env.kvx_modifiers[mod_idx][value] : NULL;
                       if (!mod) goto retry;
                       res->operands[idx].val = value;
                       res->operands[idx].type = CAT_MODIFIER;
                       res->operands[idx].mod_idx = mod_idx;
                       idx++;
                     };
                     break;
                   default:
                     fprintf (stderr,
                              "error: unexpected operand type (%s)\n",
                              type_name);
                     exit (-1);
                   };
               }
             else if (env.opc_table == kvx_kv4_v1_optab)
               {
                 switch (type)
                   {

                   case RegClass_kv4_v1_singleReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_GPR, value)
                     break;
                   case RegClass_kv4_v1_pairedReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_PGR, value)
                     break;
                   case RegClass_kv4_v1_quadReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_QGR, value)
                     break;
                   case RegClass_kv4_v1_systemReg:
                   case RegClass_kv4_v1_aloneReg:
                   case RegClass_kv4_v1_onlyraReg:
                   case RegClass_kv4_v1_onlygetReg:
                   case RegClass_kv4_v1_onlysetReg:
                   case RegClass_kv4_v1_onlyfxReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_SFR, value)
                     break;
                   case RegClass_kv4_v1_coproReg:
                   case RegClass_kv4_v1_coproReg0M4:
                   case RegClass_kv4_v1_coproReg1M4:
                   case RegClass_kv4_v1_coproReg2M4:
                   case RegClass_kv4_v1_coproReg3M4:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XCR, value)
                     break;
                   case RegClass_kv4_v1_blockReg:
                   case RegClass_kv4_v1_blockRegE:
                   case RegClass_kv4_v1_blockRegO:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XBR, value)
                     break;
                   case RegClass_kv4_v1_vectorReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XVR, value)
                     break;
                   case RegClass_kv4_v1_tileReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XTR, value)
                     break;
                   case RegClass_kv4_v1_matrixReg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_XMR, value)
                     break;
                   case RegClass_kv4_v1_buffer2Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X2R, value)
                     break;
                   case RegClass_kv4_v1_buffer4Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X4R, value)
                     break;
                   case RegClass_kv4_v1_buffer8Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X8R, value)
                     break;
                   case RegClass_kv4_v1_buffer16Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X16R, value)
                     break;
                   case RegClass_kv4_v1_buffer32Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X32R, value)
                     break;
                   case RegClass_kv4_v1_buffer64Reg:
                     KVX_PRINT_REG (KVX_REGFILE_DEC_X64R, value)
                     break;
                   case Immediate_kv4_v1_brknumber:
                   case Immediate_kv4_v1_sysnumber:
                   case Immediate_kv4_v1_signed10:
                   case Immediate_kv4_v1_signed16:
                   case Immediate_kv4_v1_signed27:
                   case Immediate_kv4_v1_wrapped32:
                   case Immediate_kv4_v1_signed37:
                   case Immediate_kv4_v1_signed43:
                   case Immediate_kv4_v1_signed54:
                   case Immediate_kv4_v1_wrapped64:
                   case Immediate_kv4_v1_unsigned6:
                     res->operands[idx].val = value;
                     res->operands[idx].sign = flags & kvxSIGNED;
                     res->operands[idx].width = width;
                     res->operands[idx].type = CAT_IMMEDIATE;
                     res->operands[idx].pcrel = 0;
                     idx++;
                     break;
                   case Immediate_kv4_v1_pcrel27:
                   case Immediate_kv4_v1_pcrel17:
                     res->operands[idx].val = value + memaddr;
                     res->operands[idx].sign = flags & kvxSIGNED;
                     res->operands[idx].width = width;
                     res->operands[idx].type = CAT_IMMEDIATE;
                     res->operands[idx].pcrel = 1;
                     idx++;
                     break;
                   case Modifier_kv4_v1_accesses:
                   case Modifier_kv4_v1_boolcas:
                   case Modifier_kv4_v1_cachelev:
                   case Modifier_kv4_v1_channel:
                   case Modifier_kv4_v1_coherency:
                   case Modifier_kv4_v1_comparison:
                   case Modifier_kv4_v1_conjugate:
                   case Modifier_kv4_v1_doscale:
                   case Modifier_kv4_v1_exunum:
                   case Modifier_kv4_v1_floatcomp:
                   case Modifier_kv4_v1_hindex:
                   case Modifier_kv4_v1_lsomask:
                   case Modifier_kv4_v1_lsumask:
                   case Modifier_kv4_v1_lsupack:
                   case Modifier_kv4_v1_qindex:
                   case Modifier_kv4_v1_rounding:
                   case Modifier_kv4_v1_scalarcond:
                   case Modifier_kv4_v1_shuffleV:
                   case Modifier_kv4_v1_shuffleX:
                   case Modifier_kv4_v1_silent:
                   case Modifier_kv4_v1_simplecond:
                   case Modifier_kv4_v1_speculate:
                   case Modifier_kv4_v1_splat32:
                   case Modifier_kv4_v1_transpose:
                   case Modifier_kv4_v1_variant:
                     {
                       int sz = 0;
                       int mod_idx = type - Modifier_kv4_v1_accesses;
                       for (sz = 0; env.kvx_modifiers[mod_idx][sz]; ++sz);
                       const char *mod = value < (unsigned) sz
                         ? env.kvx_modifiers[mod_idx][value] : NULL;
                       if (!mod) goto retry;
                       res->operands[idx].val = value;
                       res->operands[idx].type = CAT_MODIFIER;
                       res->operands[idx].mod_idx = mod_idx;
                       idx++;
                     }
                     break;
                   default:
                     fprintf (stderr, "error: unexpected operand type (%s)\n",
                              type_name);
                     exit (-1);
                   };
               }

#undef KVX_PRINT_REG
           }

         found = 1;
         break;
       retry:;
         idx = 0;
         continue;
       }
}
 res->nb_ops = idx;
 return found;
}

int
print_insn_kvx (bfd_vma memaddr, struct disassemble_info *info)
{
 static int insnindex = 0;
 static int insncount = 0;
 insn_t *insn;
 int readsofar = 0;
 int found = 0;
 int invalid_bundle = 0;

 if (!env.initialized_p)
   kvx_dis_init (info);

 /* Clear instruction information field.  */
 info->insn_info_valid = 0;
 info->branch_delay_insns = 0;
 info->data_size = 0;
 info->insn_type = dis_noninsn;
 info->target = 0;
 info->target2 = 0;

 /* Set line length.  */
 info->bytes_per_line = 16;


 /* If this is the beginning of the bundle, read BUNDLESIZE words and apply
    decentrifugate function.  */
 if (insnindex == 0)
   {
     int wordcount;
     for (wordcount = 0; wordcount < KVXMAXBUNDLEWORDS; wordcount++)
       {
         int status;
         status =
           (*info->read_memory_func) (memaddr + 4 * wordcount,
                                      (bfd_byte *) (bundle_words +
                                                    wordcount), 4, info);
         if (status != 0)
           {
             (*info->memory_error_func) (status, memaddr + 4 * wordcount,
                                         info);
             return -1;
           }
         if (!kvx_has_parallel_bit (bundle_words[wordcount]))
           break;
       }
     wordcount++;
     invalid_bundle = !kvx_reassemble_bundle (wordcount, &insncount);
   }

 assert (insnindex < KVXMAXBUNDLEISSUE);
 insn = &(bundle_insn[insnindex]);
 readsofar = insn->len * 4;
 insnindex++;

 if (opt_pretty)
   {
     (*info->fprintf_func) (info->stream, "[ ");
     for (int i = 0; i < insn->len; i++)
       (*info->fprintf_func) (info->stream, "%08x ", insn->syllables[i]);
     (*info->fprintf_func) (info->stream, "] ");
   }

 /* Check for extension to right iff this is not the end of bundle.  */

 struct decoded_insn dec;
 memset (&dec, 0, sizeof dec);
 if (!invalid_bundle && (found = decode_insn (memaddr, insn, &dec)))
   {
     int ch;
     (*info->fprintf_func) (info->stream, "%s", dec.opc->as_op);
     const char *fmtp = dec.opc->fmtstring;
     for (int i = 0; i < dec.nb_ops; ++i)
       {
         /* Print characters in the format string up to the following % or nul.  */
         while ((ch = *fmtp) && ch != '%')
           {
             (*info->fprintf_func) (info->stream, "%c", ch);
             fmtp++;
           }

         /* Skip past %s.  */
         if (ch == '%')
           {
             ch = *fmtp++;
             fmtp++;
           }

         switch (dec.operands[i].type)
           {
           case CAT_REGISTER:
             (*info->fprintf_func) (info->stream, "%s",
                                    env.kvx_registers[dec.operands[i].val].name);
             break;
           case CAT_MODIFIER:
             {
               const char *mod = env.kvx_modifiers[dec.operands[i].mod_idx][dec.operands[i].val];
               (*info->fprintf_func) (info->stream, "%s", !mod || !strcmp (mod, ".") ? "" : mod);
             }
             break;
           case CAT_IMMEDIATE:
             {
               if (dec.operands[i].pcrel)
                 {
                   /* Fill in instruction information.  */
                   info->insn_info_valid = 1;
                   info->insn_type =
                     dec.operands[i].width ==
                     17 ? dis_condbranch : dis_branch;
                   info->target = dec.operands[i].val;

                   info->print_address_func (dec.operands[i].val, info);
                 }
               else if (dec.operands[i].sign)
                 {
                   if (dec.operands[i].width <= 32)
                     {
                       (*info->fprintf_func) (info->stream, "%" PRId32 " (0x%" PRIx32 ")",
                                              (int32_t) dec.operands[i].val,
                                              (int32_t) dec.operands[i].val);
                     }
                   else
                     {
                       (*info->fprintf_func) (info->stream, "%" PRId64 " (0x%" PRIx64 ")",
                                              dec.operands[i].val,
                                              dec.operands[i].val);
                     }
                 }
               else
                 {
                   if (dec.operands[i].width <= 32)
                     {
                       (*info->fprintf_func) (info->stream, "%" PRIu32 " (0x%" PRIx32 ")",
                                              (uint32_t) dec.operands[i].
                                              val,
                                              (uint32_t) dec.operands[i].
                                              val);
                     }
                   else
                     {
                       (*info->fprintf_func) (info->stream, "%" PRIu64 " (0x%" PRIx64 ")",
                                              (uint64_t) dec.
                                              operands[i].val,
                                              (uint64_t) dec.
                                              operands[i].val);
                     }
                 }
             }
             break;
           default:
             break;

           }
       }

     while ((ch = *fmtp))
       {
         (*info->fprintf_styled_func) (info->stream, dis_style_text, "%c",
                                       ch);
         fmtp++;
       }
   }
 else
   {
     (*info->fprintf_func) (info->stream, "*** invalid opcode ***\n");
     insnindex = 0;
     readsofar = 4;
   }

 if (found && (insnindex == insncount))
   {
     (*info->fprintf_func) (info->stream, ";;");
     if (!opt_compact_assembly)
       (*info->fprintf_func) (info->stream, "\n");
     insnindex = 0;
   }

 return readsofar;
}

/* This function searches in the current bundle for the instructions required
  by unwinding. For prologue:
    (1) addd $r12 = $r12, <res_stack>
    (2) get <gpr_ra_reg> = $ra
    (3) sd <ofs>[$r12] = <gpr_ra_reg> or sq/so containing <gpr_ra_reg>
    (4) sd <ofs>[$r12] = $r14 or sq/so containing r14
    (5) addd $r14 = $r12, <fp_ofs> or copyd $r14 = $r12
        The only difference seen between the code generated by gcc and clang
        is the setting/resetting r14. gcc could also generate copyd $r14=$r12
        instead of add addd $r14 = $r12, <ofs> when <ofs> is 0.
        Vice-versa, <ofs> is not guaranteed to be 0 for clang, so, clang
        could also generate addd instead of copyd
    (6) call, icall, goto, igoto, cb., ret
 For epilogue:
    (1) addd $r12 = $r12, <res_stack>
    (2) addd $r12 = $r14, <offset> or copyd $r12 = $r14
        Same comment as prologue (5).
    (3) ret, goto
    (4) call, icall, igoto, cb.  */

int
decode_prologue_epilogue_bundle (bfd_vma memaddr,
                                struct disassemble_info *info,
                                struct kvx_prologue_epilogue_bundle *peb)
{
 int i, nb_insn, nb_syl;

 peb->nb_insn = 0;

 if (info->arch != bfd_arch_kvx)
   return -1;

 if (!env.initialized_p)
   kvx_dis_init (info);

 /* Read the bundle.  */
 for (nb_syl = 0; nb_syl < KVXMAXBUNDLEWORDS; nb_syl++)
   {
     if ((*info->read_memory_func) (memaddr + 4 * nb_syl,
                                    (bfd_byte *) &bundle_words[nb_syl], 4,
                                    info))
       return -1;
     if (!kvx_has_parallel_bit (bundle_words[nb_syl]))
       break;
   }
 nb_syl++;
 if (!kvx_reassemble_bundle (nb_syl, &nb_insn))
   return -1;

 /* Check for extension to right if this is not the end of bundle
    find the format of this insn.  */
 for (int idx_insn = 0; idx_insn < nb_insn; idx_insn++)
   {
     insn_t *insn = &bundle_insn[idx_insn];
     int is_add = 0, is_get = 0, is_a_peb_insn = 0, is_copyd = 0;

     struct decoded_insn dec;
     memset (&dec, 0, sizeof dec);
     if (!decode_insn (memaddr, insn, &dec))
       continue;

     const char *op_name = dec.opc->as_op;
     struct kvx_prologue_epilogue_insn *crt_peb_insn;

     crt_peb_insn = &peb->insn[peb->nb_insn];
     crt_peb_insn->nb_gprs = 0;

     if (!strcmp (op_name, "addd"))
       is_add = 1;
     else if (!strcmp (op_name, "copyd"))
       is_copyd = 1;
     else if (!strcmp (op_name, "get"))
       is_get = 1;
     else if (!strcmp (op_name, "sd"))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_SD;
         is_a_peb_insn = 1;
       }
     else if (!strcmp (op_name, "sq"))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_SQ;
         is_a_peb_insn = 1;
       }
     else if (!strcmp (op_name, "so"))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_SO;
         is_a_peb_insn = 1;
       }
     else if (!strcmp (op_name, "ret"))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_RET;
         is_a_peb_insn = 1;
       }
     else if (!strcmp (op_name, "goto"))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_GOTO;
         is_a_peb_insn = 1;
       }
     else if (!strcmp (op_name, "igoto"))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_IGOTO;
         is_a_peb_insn = 1;
       }
     else if (!strcmp (op_name, "call") || !strcmp (op_name, "icall"))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_CALL;
         is_a_peb_insn = 1;
       }
     else if (!strncmp (op_name, "cb", 2))
       {
         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_CB;
         is_a_peb_insn = 1;
       }
     else
       continue;

     for (i = 0; dec.opc->format[i]; i++)
       {
         struct kvx_operand *fmt = dec.opc->format[i];
         struct kvx_bitfield *bf = fmt->bfield;
         int bf_nb = fmt->bitfields;
         int width = fmt->width;
         int type = fmt->type;
         int flags = fmt->flags;
         int shift = fmt->shift;
         int bias = fmt->bias;
         uint64_t encoded_value, value = 0;

         for (int bf_idx = 0; bf_idx < bf_nb; bf_idx++)
           {
             int insn_idx = (int) bf[bf_idx].to_offset / 32;
             int to_offset = bf[bf_idx].to_offset % 32;
             encoded_value = insn->syllables[insn_idx] >> to_offset;
             encoded_value &= (1LL << bf[bf_idx].size) - 1;
             value |= encoded_value << bf[bf_idx].from_offset;
           }
         if (flags & kvxSIGNED)
           {
             uint64_t signbit = 1LL << (width - 1);
             value = (value ^ signbit) - signbit;
           }
         value = (value << shift) + bias;

#define chk_type(core_, val_) \
     (env.opc_table == kvx_## core_ ##_optab && type == (val_))

         if (chk_type (kv3_v1, RegClass_kv3_v1_singleReg)
             || chk_type (kv3_v2, RegClass_kv3_v2_singleReg)
             || chk_type (kv4_v1, RegClass_kv4_v1_singleReg))
           {
             if (env.kvx_regfiles[KVX_REGFILE_DEC_GPR] + value
                 >= env.kvx_max_dec_registers)
               return -1;
             if (is_add && i < 2)
               {
                 if (i == 0)
                   {
                     if (value == KVX_GPR_REG_SP)
                       crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_ADD_SP;
                     else if (value == KVX_GPR_REG_FP)
                       crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_ADD_FP;
                     else
                       is_add = 0;
                   }
                 else if (i == 1)
                   {
                     if (value == KVX_GPR_REG_SP)
                       is_a_peb_insn = 1;
                     else if (value == KVX_GPR_REG_FP
                              && crt_peb_insn->insn_type
                              == KVX_PROL_EPIL_INSN_ADD_SP)
                       {
                         crt_peb_insn->insn_type
                           = KVX_PROL_EPIL_INSN_RESTORE_SP_FROM_FP;
                         is_a_peb_insn = 1;
                       }
                     else
                       is_add = 0;
                   }
               }
             else if (is_copyd && i < 2)
               {
                 if (i == 0)
                   {
                     if (value == KVX_GPR_REG_FP)
                       {
                         crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_ADD_FP;
                         crt_peb_insn->immediate = 0;
                       }
                     else if (value == KVX_GPR_REG_SP)
                       {
                         crt_peb_insn->insn_type
                           = KVX_PROL_EPIL_INSN_RESTORE_SP_FROM_FP;
                         crt_peb_insn->immediate = 0;
                       }
                     else
                       is_copyd = 0;
                   }
                 else if (i == 1)
                   {
                     if (value == KVX_GPR_REG_SP
                         && crt_peb_insn->insn_type
                         == KVX_PROL_EPIL_INSN_ADD_FP)
                       is_a_peb_insn = 1;
                     else if (value == KVX_GPR_REG_FP
                              && crt_peb_insn->insn_type
                              == KVX_PROL_EPIL_INSN_RESTORE_SP_FROM_FP)
                       is_a_peb_insn = 1;
                     else
                       is_copyd = 0;
                   }
               }
             else
               crt_peb_insn->gpr_reg[crt_peb_insn->nb_gprs++] = value;
           }
         else if (chk_type (kv3_v1, RegClass_kv3_v1_pairedReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_pairedReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_pairedReg))
           crt_peb_insn->gpr_reg[crt_peb_insn->nb_gprs++] = value * 2;
         else if (chk_type (kv3_v1, RegClass_kv3_v1_quadReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_quadReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_quadReg))
           crt_peb_insn->gpr_reg[crt_peb_insn->nb_gprs++] = value * 4;
         else if (chk_type (kv3_v1, RegClass_kv3_v1_systemReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_systemReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_systemReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_aloneReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_aloneReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_aloneReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_onlyraReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_onlyraReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_onlygetReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_onlygetReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_onlygetReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_onlygetReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_onlysetReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_onlysetReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_onlysetReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_onlyfxReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_onlyfxReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_onlyfxReg))
           {
             if (env.kvx_regfiles[KVX_REGFILE_DEC_GPR] + value
                 >= env.kvx_max_dec_registers)
               return -1;
             if (is_get && !strcmp (env.kvx_registers[env.kvx_dec_registers[env.kvx_regfiles[KVX_REGFILE_DEC_SFR] + value]].name, "$ra"))
               {
                 crt_peb_insn->insn_type = KVX_PROL_EPIL_INSN_GET_RA;
                 is_a_peb_insn = 1;
               }
           }
         else if (chk_type (kv3_v1, RegClass_kv3_v1_coproReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_coproReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_coproReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_blockReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_blockReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_blockReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_vectorReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_vectorReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_vectorReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_tileReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_tileReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_tileReg)
                  || chk_type (kv3_v1, RegClass_kv3_v1_matrixReg)
                  || chk_type (kv3_v2, RegClass_kv3_v2_matrixReg)
                  || chk_type (kv4_v1, RegClass_kv4_v1_matrixReg)
                  || chk_type (kv3_v1, Modifier_kv3_v1_scalarcond)
                  || chk_type (kv3_v1, Modifier_kv3_v1_column)
                  || chk_type (kv3_v1, Modifier_kv3_v1_comparison)
                  || chk_type (kv3_v1, Modifier_kv3_v1_doscale)
                  || chk_type (kv3_v1, Modifier_kv3_v1_exunum)
                  || chk_type (kv3_v1, Modifier_kv3_v1_floatcomp)
                  || chk_type (kv3_v1, Modifier_kv3_v1_qindex)
                  || chk_type (kv3_v1, Modifier_kv3_v1_rectify)
                  || chk_type (kv3_v1, Modifier_kv3_v1_rounding)
                  || chk_type (kv3_v1, Modifier_kv3_v1_roundint)
                  || chk_type (kv3_v1, Modifier_kv3_v1_saturate)
                  || chk_type (kv3_v1, Modifier_kv3_v1_scalarcond)
                  || chk_type (kv3_v1, Modifier_kv3_v1_silent)
                  || chk_type (kv3_v1, Modifier_kv3_v1_simplecond)
                  || chk_type (kv3_v1, Modifier_kv3_v1_speculate)
                  || chk_type (kv3_v1, Modifier_kv3_v1_splat32)
                  || chk_type (kv3_v1, Modifier_kv3_v1_variant)
                  || chk_type (kv3_v2, Modifier_kv3_v2_accesses)
                  || chk_type (kv3_v2, Modifier_kv3_v2_boolcas)
                  || chk_type (kv3_v2, Modifier_kv3_v2_cachelev)
                  || chk_type (kv3_v2, Modifier_kv3_v2_channel)
                  || chk_type (kv3_v2, Modifier_kv3_v2_coherency)
                  || chk_type (kv3_v2, Modifier_kv3_v2_comparison)
                  || chk_type (kv3_v2, Modifier_kv3_v2_conjugate)
                  || chk_type (kv3_v2, Modifier_kv3_v2_doscale)
                  || chk_type (kv3_v2, Modifier_kv3_v2_exunum)
                  || chk_type (kv3_v2, Modifier_kv3_v2_floatcomp)
                  || chk_type (kv3_v2, Modifier_kv3_v2_hindex)
                  || chk_type (kv3_v2, Modifier_kv3_v2_lsomask)
                  || chk_type (kv3_v2, Modifier_kv3_v2_lsumask)
                  || chk_type (kv3_v2, Modifier_kv3_v2_lsupack)
                  || chk_type (kv3_v2, Modifier_kv3_v2_qindex)
                  || chk_type (kv3_v2, Modifier_kv3_v2_rounding)
                  || chk_type (kv3_v2, Modifier_kv3_v2_scalarcond)
                  || chk_type (kv3_v2, Modifier_kv3_v2_shuffleV)
                  || chk_type (kv3_v2, Modifier_kv3_v2_shuffleX)
                  || chk_type (kv3_v2, Modifier_kv3_v2_silent)
                  || chk_type (kv3_v2, Modifier_kv3_v2_simplecond)
                  || chk_type (kv3_v2, Modifier_kv3_v2_speculate)
                  || chk_type (kv3_v2, Modifier_kv3_v2_splat32)
                  || chk_type (kv3_v2, Modifier_kv3_v2_transpose)
                  || chk_type (kv3_v2, Modifier_kv3_v2_variant)
                  || chk_type (kv4_v1, Modifier_kv4_v1_accesses)
                  || chk_type (kv4_v1, Modifier_kv4_v1_boolcas)
                  || chk_type (kv4_v1, Modifier_kv4_v1_cachelev)
                  || chk_type (kv4_v1, Modifier_kv4_v1_channel)
                  || chk_type (kv4_v1, Modifier_kv4_v1_coherency)
                  || chk_type (kv4_v1, Modifier_kv4_v1_comparison)
                  || chk_type (kv4_v1, Modifier_kv4_v1_conjugate)
                  || chk_type (kv4_v1, Modifier_kv4_v1_doscale)
                  || chk_type (kv4_v1, Modifier_kv4_v1_exunum)
                  || chk_type (kv4_v1, Modifier_kv4_v1_floatcomp)
                  || chk_type (kv4_v1, Modifier_kv4_v1_hindex)
                  || chk_type (kv4_v1, Modifier_kv4_v1_lsomask)
                  || chk_type (kv4_v1, Modifier_kv4_v1_lsumask)
                  || chk_type (kv4_v1, Modifier_kv4_v1_lsupack)
                  || chk_type (kv4_v1, Modifier_kv4_v1_qindex)
                  || chk_type (kv4_v1, Modifier_kv4_v1_rounding)
                  || chk_type (kv4_v1, Modifier_kv4_v1_scalarcond)
                  || chk_type (kv4_v1, Modifier_kv4_v1_shuffleV)
                  || chk_type (kv4_v1, Modifier_kv4_v1_shuffleX)
                  || chk_type (kv4_v1, Modifier_kv4_v1_silent)
                  || chk_type (kv4_v1, Modifier_kv4_v1_simplecond)
                  || chk_type (kv4_v1, Modifier_kv4_v1_speculate)
                  || chk_type (kv4_v1, Modifier_kv4_v1_splat32)
                  || chk_type (kv4_v1, Modifier_kv4_v1_transpose)
                  || chk_type (kv4_v1, Modifier_kv4_v1_variant))
           {
             /* Do nothing.  */
           }
         else if (chk_type (kv3_v1, Immediate_kv3_v1_sysnumber)
                  || chk_type (kv3_v2, Immediate_kv3_v2_sysnumber)
                  || chk_type (kv4_v1, Immediate_kv4_v1_sysnumber)
                  || chk_type (kv3_v2, Immediate_kv3_v2_wrapped8)
                  || chk_type (kv4_v1, Immediate_kv4_v1_wrapped8)
                  || chk_type (kv3_v1, Immediate_kv3_v1_signed10)
                  || chk_type (kv3_v2, Immediate_kv3_v2_signed10)
                  || chk_type (kv4_v1, Immediate_kv4_v1_signed10)
                  || chk_type (kv3_v1, Immediate_kv3_v1_signed16)
                  || chk_type (kv3_v2, Immediate_kv3_v2_signed16)
                  || chk_type (kv4_v1, Immediate_kv4_v1_signed16)
                  || chk_type (kv3_v1, Immediate_kv3_v1_signed27)
                  || chk_type (kv3_v2, Immediate_kv3_v2_signed27)
                  || chk_type (kv4_v1, Immediate_kv4_v1_signed27)
                  || chk_type (kv3_v1, Immediate_kv3_v1_wrapped32)
                  || chk_type (kv3_v2, Immediate_kv3_v2_wrapped32)
                  || chk_type (kv4_v1, Immediate_kv4_v1_wrapped32)
                  || chk_type (kv3_v1, Immediate_kv3_v1_signed37)
                  || chk_type (kv3_v2, Immediate_kv3_v2_signed37)
                  || chk_type (kv4_v1, Immediate_kv4_v1_signed37)
                  || chk_type (kv3_v1, Immediate_kv3_v1_signed43)
                  || chk_type (kv3_v2, Immediate_kv3_v2_signed43)
                  || chk_type (kv4_v1, Immediate_kv4_v1_signed43)
                  || chk_type (kv3_v1, Immediate_kv3_v1_signed54)
                  || chk_type (kv3_v2, Immediate_kv3_v2_signed54)
                  || chk_type (kv4_v1, Immediate_kv4_v1_signed54)
                  || chk_type (kv3_v1, Immediate_kv3_v1_wrapped64)
                  || chk_type (kv3_v2, Immediate_kv3_v2_wrapped64)
                  || chk_type (kv4_v1, Immediate_kv4_v1_wrapped64)
                  || chk_type (kv3_v1, Immediate_kv3_v1_unsigned6)
                  || chk_type (kv3_v2, Immediate_kv3_v2_unsigned6)
                  || chk_type (kv4_v1, Immediate_kv4_v1_unsigned6))
           crt_peb_insn->immediate = value;
         else if (chk_type (kv3_v1, Immediate_kv3_v1_pcrel17)
                  || chk_type (kv3_v2, Immediate_kv3_v2_pcrel17)
                  || chk_type (kv4_v1, Immediate_kv4_v1_pcrel17)
                  || chk_type (kv3_v1, Immediate_kv3_v1_pcrel27)
                  || chk_type (kv3_v2, Immediate_kv3_v2_pcrel27)
                  || chk_type (kv4_v1, Immediate_kv4_v1_pcrel27))
           crt_peb_insn->immediate = value + memaddr;
         else
           return -1;
       }

     if (is_a_peb_insn)
       peb->nb_insn++;
     continue;
   }

 return nb_syl * 4;
#undef chk_type
}

void
print_kvx_disassembler_options (FILE * stream)
{
 fprintf (stream, _("\n\
The following KVX specific disassembler options are supported for use\n\
with the -M switch (multiple options should be separated by commas):\n"));

 fprintf (stream, _("\n\
 pretty               Print 32-bit words in natural order corresponding to \
re-ordered instruction.\n"));

 fprintf (stream, _("\n\
 compact-assembly     Do not emit a new line between bundles of instructions.\
\n"));

 fprintf (stream, _("\n\
 no-compact-assembly  Emit a new line between bundles of instructions.\n"));

 fprintf (stream, _("\n"));
}