Home | History | Annotate | Line # | Download | only in opcodes
      1      1.1  christos /* LoongArch opcode support.
      2  1.1.1.4  christos    Copyright (C) 2021-2026 Free Software Foundation, Inc.
      3      1.1  christos    Contributed by Loongson Ltd.
      4      1.1  christos 
      5      1.1  christos    This file is part of the GNU opcodes library.
      6      1.1  christos 
      7      1.1  christos    This library is free software; you can redistribute it and/or modify
      8      1.1  christos    it under the terms of the GNU General Public License as published by
      9      1.1  christos    the Free Software Foundation; either version 3, or (at your option)
     10      1.1  christos    any later version.
     11      1.1  christos 
     12      1.1  christos    It is distributed in the hope that it will be useful, but WITHOUT
     13      1.1  christos    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     14      1.1  christos    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     15      1.1  christos    License for more details.
     16      1.1  christos 
     17      1.1  christos    You should have received a copy of the GNU General Public License
     18      1.1  christos    along with this program; see the file COPYING3.  If not,
     19      1.1  christos    see <http://www.gnu.org/licenses/>.  */
     20      1.1  christos 
     21      1.1  christos #include "sysdep.h"
     22      1.1  christos #include "disassemble.h"
     23      1.1  christos #include "opintl.h"
     24      1.1  christos #include "opcode/loongarch.h"
     25      1.1  christos #include "libiberty.h"
     26      1.1  christos #include <stdlib.h>
     27      1.1  christos 
     28  1.1.1.2  christos static bool loongarch_dis_show_aliases = true;
     29  1.1.1.2  christos static const char *const *loongarch_r_disname = NULL;
     30  1.1.1.2  christos static const char *const *loongarch_f_disname = NULL;
     31  1.1.1.2  christos static const char *const *loongarch_fc_disname = NULL;
     32  1.1.1.2  christos static const char *const *loongarch_c_disname = NULL;
     33  1.1.1.2  christos static const char *const *loongarch_cr_disname = NULL;
     34  1.1.1.2  christos static const char *const *loongarch_v_disname = NULL;
     35  1.1.1.2  christos static const char *const *loongarch_x_disname = NULL;
     36  1.1.1.2  christos 
     37      1.1  christos static const struct loongarch_opcode *
     38      1.1  christos get_loongarch_opcode_by_binfmt (insn_t insn)
     39      1.1  christos {
     40      1.1  christos   const struct loongarch_opcode *it;
     41      1.1  christos   struct loongarch_ase *ase;
     42      1.1  christos   size_t i;
     43      1.1  christos   for (ase = loongarch_ASEs; ase->enabled; ase++)
     44      1.1  christos     {
     45      1.1  christos       if (!*ase->enabled || (ase->include && !*ase->include)
     46      1.1  christos 	  || (ase->exclude && *ase->exclude))
     47      1.1  christos 	continue;
     48      1.1  christos 
     49      1.1  christos       if (!ase->opc_htab_inited)
     50      1.1  christos 	{
     51      1.1  christos 	  for (it = ase->opcodes; it->mask; it++)
     52      1.1  christos 	    if (!ase->opc_htab[LARCH_INSN_OPC (it->match)]
     53  1.1.1.2  christos 		&& it->macro == NULL
     54  1.1.1.2  christos 		&& (!(it->pinfo & INSN_DIS_ALIAS)
     55  1.1.1.2  christos 		    || loongarch_dis_show_aliases))
     56      1.1  christos 	      ase->opc_htab[LARCH_INSN_OPC (it->match)] = it;
     57      1.1  christos 	  for (i = 0; i < 16; i++)
     58      1.1  christos 	    if (!ase->opc_htab[i])
     59      1.1  christos 	      ase->opc_htab[i] = it;
     60      1.1  christos 	  ase->opc_htab_inited = 1;
     61      1.1  christos 	}
     62      1.1  christos 
     63      1.1  christos       it = ase->opc_htab[LARCH_INSN_OPC (insn)];
     64      1.1  christos       for (; it->name; it++)
     65      1.1  christos 	if ((insn & it->mask) == it->match && it->mask
     66      1.1  christos 	    && !(it->include && !*it->include)
     67      1.1  christos 	    && !(it->exclude && *it->exclude))
     68  1.1.1.4  christos 	  {
     69  1.1.1.4  christos 	    /* ud ui5 need rd==rj. We should continue searching
     70  1.1.1.4  christos 	       for the next `it` if rd != rj. Furthermore, we need
     71  1.1.1.4  christos 	       `it->pinfo` to ensure that only the `it` in loongarch
     72  1.1.1.4  christos 	       alias_opcodes[] is skipped.  */
     73  1.1.1.4  christos 	    if (LARCH_INSN_AMSWAP_W (insn)
     74  1.1.1.4  christos 		&& (LARCH_GET_RD (insn) != LARCH_GET_RJ (insn))
     75  1.1.1.4  christos 		&& (it->pinfo & INSN_DIS_ALIAS))
     76  1.1.1.4  christos 	      continue;
     77  1.1.1.4  christos 	    return it;
     78  1.1.1.4  christos 	  }
     79      1.1  christos     }
     80      1.1  christos   return NULL;
     81      1.1  christos }
     82      1.1  christos 
     83      1.1  christos static void
     84      1.1  christos set_default_loongarch_dis_options (void)
     85      1.1  christos {
     86      1.1  christos   LARCH_opts.ase_ilp32 = 1;
     87      1.1  christos   LARCH_opts.ase_lp64 = 1;
     88      1.1  christos   LARCH_opts.ase_sf = 1;
     89      1.1  christos   LARCH_opts.ase_df = 1;
     90      1.1  christos   LARCH_opts.ase_lsx = 1;
     91      1.1  christos   LARCH_opts.ase_lasx = 1;
     92  1.1.1.2  christos   LARCH_opts.ase_lvz = 1;
     93  1.1.1.2  christos   LARCH_opts.ase_lbt = 1;
     94      1.1  christos 
     95  1.1.1.2  christos   loongarch_r_disname = loongarch_r_alias;
     96  1.1.1.2  christos   loongarch_f_disname = loongarch_f_alias;
     97  1.1.1.2  christos   loongarch_fc_disname = loongarch_fc_normal_name;
     98      1.1  christos   loongarch_c_disname = loongarch_c_normal_name;
     99      1.1  christos   loongarch_cr_disname = loongarch_cr_normal_name;
    100      1.1  christos   loongarch_v_disname = loongarch_v_normal_name;
    101      1.1  christos   loongarch_x_disname = loongarch_x_normal_name;
    102      1.1  christos }
    103      1.1  christos 
    104      1.1  christos static int
    105      1.1  christos parse_loongarch_dis_option (const char *option)
    106      1.1  christos {
    107  1.1.1.2  christos   if (strcmp (option, "no-aliases") == 0)
    108  1.1.1.3  christos     {
    109  1.1.1.3  christos       loongarch_dis_show_aliases = false;
    110  1.1.1.3  christos       return 0;
    111  1.1.1.3  christos     }
    112  1.1.1.2  christos 
    113      1.1  christos   if (strcmp (option, "numeric") == 0)
    114      1.1  christos     {
    115      1.1  christos       loongarch_r_disname = loongarch_r_normal_name;
    116      1.1  christos       loongarch_f_disname = loongarch_f_normal_name;
    117  1.1.1.3  christos       return 0;
    118      1.1  christos     }
    119  1.1.1.3  christos 
    120      1.1  christos   return -1;
    121      1.1  christos }
    122      1.1  christos 
    123      1.1  christos static int
    124      1.1  christos parse_loongarch_dis_options (const char *opts_in)
    125      1.1  christos {
    126      1.1  christos   set_default_loongarch_dis_options ();
    127      1.1  christos 
    128      1.1  christos   if (opts_in == NULL)
    129      1.1  christos     return 0;
    130      1.1  christos 
    131      1.1  christos   char *opts, *opt, *opt_end;
    132      1.1  christos   opts = xmalloc (strlen (opts_in) + 1);
    133      1.1  christos   strcpy (opts, opts_in);
    134      1.1  christos 
    135      1.1  christos   for (opt = opt_end = opts; opt_end != NULL; opt = opt_end + 1)
    136      1.1  christos     {
    137      1.1  christos       if ((opt_end = strchr (opt, ',')) != NULL)
    138      1.1  christos 	*opt_end = 0;
    139      1.1  christos       if (parse_loongarch_dis_option (opt) != 0)
    140      1.1  christos 	return -1;
    141      1.1  christos     }
    142      1.1  christos   free (opts);
    143      1.1  christos   return 0;
    144      1.1  christos }
    145      1.1  christos 
    146      1.1  christos static int32_t
    147      1.1  christos dis_one_arg (char esc1, char esc2, const char *bit_field,
    148      1.1  christos 	     const char *arg ATTRIBUTE_UNUSED, void *context)
    149      1.1  christos {
    150      1.1  christos   static int need_comma = 0;
    151      1.1  christos   struct disassemble_info *info = context;
    152      1.1  christos   insn_t insn = *(insn_t *) info->private_data;
    153      1.1  christos   int32_t imm, u_imm;
    154  1.1.1.2  christos   enum disassembler_style style;
    155  1.1.1.4  christos   bool is_ud_2nd_arg = false;
    156  1.1.1.4  christos 
    157  1.1.1.4  christos   if (LARCH_INSN_AMSWAP_W (insn)
    158  1.1.1.4  christos       && (LARCH_GET_RD (insn) == LARCH_GET_RJ (insn))
    159  1.1.1.4  christos       && (LARCH_GET_RK (insn) == 1)
    160  1.1.1.4  christos       && loongarch_dis_show_aliases
    161  1.1.1.4  christos       && need_comma)
    162  1.1.1.4  christos     is_ud_2nd_arg = true;
    163      1.1  christos 
    164      1.1  christos   if (esc1)
    165      1.1  christos     {
    166  1.1.1.4  christos       /* The "ud ui5" does not nedd a comma.  */
    167  1.1.1.4  christos       if (need_comma && !is_ud_2nd_arg)
    168  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, ", ");
    169      1.1  christos       need_comma = 1;
    170      1.1  christos       imm = loongarch_decode_imm (bit_field, insn, 1);
    171      1.1  christos       u_imm = loongarch_decode_imm (bit_field, insn, 0);
    172      1.1  christos     }
    173      1.1  christos 
    174      1.1  christos   switch (esc1)
    175      1.1  christos     {
    176      1.1  christos     case 'r':
    177  1.1.1.4  christos       switch (esc2)
    178  1.1.1.4  christos 	{
    179  1.1.1.4  christos 	  case 'u':
    180  1.1.1.4  christos 	    /* The "ud ui5" only needs to print one parameter.  */
    181  1.1.1.4  christos 	    if (is_ud_2nd_arg)
    182  1.1.1.4  christos 	      break;
    183  1.1.1.4  christos 	    info->fprintf_styled_func (info->stream, dis_style_immediate, "0x%x", u_imm);
    184  1.1.1.4  christos 	    break;
    185  1.1.1.4  christos 	  default:
    186  1.1.1.4  christos 	    info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_r_disname[u_imm]);
    187  1.1.1.4  christos 	}
    188      1.1  christos       break;
    189      1.1  christos     case 'f':
    190  1.1.1.2  christos       switch (esc2)
    191  1.1.1.2  christos 	{
    192  1.1.1.2  christos 	case 'c':
    193  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_fc_disname[u_imm]);
    194  1.1.1.2  christos 	  break;
    195  1.1.1.2  christos 	default:
    196  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_f_disname[u_imm]);
    197  1.1.1.2  christos 	}
    198      1.1  christos       break;
    199      1.1  christos     case 'c':
    200      1.1  christos       switch (esc2)
    201      1.1  christos 	{
    202      1.1  christos 	case 'r':
    203  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_cr_disname[u_imm]);
    204      1.1  christos 	  break;
    205      1.1  christos 	default:
    206  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_c_disname[u_imm]);
    207      1.1  christos 	}
    208      1.1  christos       break;
    209      1.1  christos     case 'v':
    210  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_v_disname[u_imm]);
    211      1.1  christos       break;
    212      1.1  christos     case 'x':
    213  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_x_disname[u_imm]);
    214      1.1  christos       break;
    215      1.1  christos     case 'u':
    216  1.1.1.2  christos       style = esc2 == 'o' ? dis_style_address_offset : dis_style_immediate;
    217  1.1.1.2  christos       info->fprintf_styled_func (info->stream, style, "0x%x", u_imm);
    218      1.1  christos       break;
    219      1.1  christos     case 's':
    220  1.1.1.2  christos       switch (esc2)
    221  1.1.1.2  christos 	{
    222  1.1.1.2  christos 	case 'b':
    223  1.1.1.2  christos 	case 'o':
    224  1.1.1.2  christos 	  /* Both represent address offsets.  */
    225  1.1.1.2  christos 	  style = dis_style_address_offset;
    226  1.1.1.2  christos 	  break;
    227  1.1.1.2  christos 	default:
    228  1.1.1.2  christos 	  style = dis_style_immediate;
    229  1.1.1.2  christos 	  break;
    230  1.1.1.2  christos 	}
    231  1.1.1.2  christos       info->fprintf_styled_func (info->stream, style, "%d", imm);
    232      1.1  christos       switch (esc2)
    233      1.1  christos 	{
    234      1.1  christos 	case 'b':
    235      1.1  christos 	  info->insn_type = dis_branch;
    236      1.1  christos 	  info->target += imm;
    237      1.1  christos 	}
    238      1.1  christos       break;
    239      1.1  christos     case '\0':
    240      1.1  christos       need_comma = 0;
    241      1.1  christos     }
    242      1.1  christos   return 0;
    243      1.1  christos }
    244      1.1  christos 
    245      1.1  christos static void
    246      1.1  christos disassemble_one (insn_t insn, struct disassemble_info *info)
    247      1.1  christos {
    248      1.1  christos   const struct loongarch_opcode *opc = get_loongarch_opcode_by_binfmt (insn);
    249      1.1  christos 
    250      1.1  christos #ifdef LOONGARCH_DEBUG
    251      1.1  christos   char have_space[32] = { 0 };
    252      1.1  christos   insn_t t;
    253      1.1  christos   int i;
    254      1.1  christos   const char *t_f = opc ? opc->format : NULL;
    255      1.1  christos   if (t_f)
    256      1.1  christos     while (*t_f)
    257      1.1  christos       {
    258      1.1  christos 	while (('a' <= t_f[0] && t_f[0] <= 'z')
    259      1.1  christos 	       || ('A' <= t_f[0] && t_f[0] <= 'Z')
    260      1.1  christos 	       || t_f[0] == ',')
    261      1.1  christos 	  t_f++;
    262      1.1  christos 	while (1)
    263      1.1  christos 	  {
    264      1.1  christos 	    i = strtol (t_f, &t_f, 10);
    265      1.1  christos 	    have_space[i] = 1;
    266      1.1  christos 	    t_f++; /* ':' */
    267      1.1  christos 	    i += strtol (t_f, &t_f, 10);
    268      1.1  christos 	    have_space[i] = 1;
    269      1.1  christos 	    if (t_f[0] == '|')
    270      1.1  christos 	      t_f++;
    271      1.1  christos 	    else
    272      1.1  christos 	      break;
    273      1.1  christos 	  }
    274      1.1  christos 	if (t_f[0] == '<')
    275      1.1  christos 	  t_f += 2; /* '<' '<' */
    276      1.1  christos 	strtol (t_f, &t_f, 10);
    277      1.1  christos       }
    278      1.1  christos 
    279      1.1  christos   have_space[28] = 1;
    280      1.1  christos   have_space[0] = 0;
    281      1.1  christos   t = ~((insn_t) -1 >> 1);
    282      1.1  christos   for (i = 31; 0 <= i; i--)
    283      1.1  christos     {
    284      1.1  christos       if (t & insn)
    285  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, "1");
    286      1.1  christos       else
    287  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, "0");
    288      1.1  christos       if (have_space[i])
    289  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, " ");
    290      1.1  christos       t = t >> 1;
    291      1.1  christos     }
    292  1.1.1.2  christos   info->fprintf_styled_func (info->stream, dis_style_text, "\t");
    293      1.1  christos #endif
    294      1.1  christos 
    295      1.1  christos   if (!opc)
    296      1.1  christos     {
    297      1.1  christos       info->insn_type = dis_noninsn;
    298  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_assembler_directive, ".word\t\t");
    299  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_immediate, "0x%08x", insn);
    300      1.1  christos       return;
    301      1.1  christos     }
    302      1.1  christos 
    303      1.1  christos   info->insn_type = dis_nonbranch;
    304  1.1.1.2  christos   if (opc->format == NULL || opc->format[0] == '\0')
    305  1.1.1.2  christos     info->fprintf_styled_func (info->stream, dis_style_mnemonic,
    306  1.1.1.2  christos 				"%s", opc->name);
    307  1.1.1.2  christos   else
    308  1.1.1.2  christos     info->fprintf_styled_func (info->stream, dis_style_mnemonic,
    309  1.1.1.2  christos 				"%-12s", opc->name);
    310      1.1  christos 
    311      1.1  christos   {
    312      1.1  christos     char *fake_args = xmalloc (strlen (opc->format) + 1);
    313      1.1  christos     const char *fake_arg_strs[MAX_ARG_NUM_PLUS_2];
    314      1.1  christos     strcpy (fake_args, opc->format);
    315      1.1  christos     if (0 < loongarch_split_args_by_comma (fake_args, fake_arg_strs))
    316  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_text, "\t");
    317      1.1  christos     info->private_data = &insn;
    318      1.1  christos     loongarch_foreach_args (opc->format, fake_arg_strs, dis_one_arg, info);
    319      1.1  christos     free (fake_args);
    320      1.1  christos   }
    321      1.1  christos 
    322      1.1  christos   if (info->insn_type == dis_branch || info->insn_type == dis_condbranch)
    323      1.1  christos     {
    324  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_comment_start, "\t# ");
    325      1.1  christos       info->print_address_func (info->target, info);
    326      1.1  christos     }
    327      1.1  christos }
    328      1.1  christos 
    329      1.1  christos int
    330      1.1  christos print_insn_loongarch (bfd_vma memaddr, struct disassemble_info *info)
    331      1.1  christos {
    332      1.1  christos   insn_t insn;
    333      1.1  christos   int status;
    334      1.1  christos 
    335      1.1  christos   static int not_init_yet = 1;
    336      1.1  christos   if (not_init_yet)
    337      1.1  christos     {
    338      1.1  christos       parse_loongarch_dis_options (info->disassembler_options);
    339      1.1  christos       not_init_yet = 0;
    340      1.1  christos     }
    341      1.1  christos 
    342      1.1  christos   info->bytes_per_chunk = 4;
    343      1.1  christos   info->bytes_per_line = 4;
    344      1.1  christos   info->display_endian = BFD_ENDIAN_LITTLE;
    345      1.1  christos   info->insn_info_valid = 1;
    346      1.1  christos   info->target = memaddr;
    347      1.1  christos 
    348      1.1  christos   if ((status = info->read_memory_func (memaddr, (bfd_byte *) &insn,
    349      1.1  christos 					sizeof (insn), info)) != 0)
    350      1.1  christos     {
    351      1.1  christos       info->memory_error_func (status, memaddr, info);
    352      1.1  christos       return -1; /* loongarch_insn_length (0); */
    353      1.1  christos     }
    354      1.1  christos 
    355      1.1  christos   disassemble_one (insn, info);
    356      1.1  christos 
    357      1.1  christos   return loongarch_insn_length (insn);
    358      1.1  christos }
    359      1.1  christos 
    360      1.1  christos void
    361      1.1  christos print_loongarch_disassembler_options (FILE *stream)
    362      1.1  christos {
    363      1.1  christos   fprintf (stream, _("\n\
    364      1.1  christos The following LoongArch disassembler options are supported for use\n\
    365      1.1  christos with the -M switch (multiple options should be separated by commas):\n"));
    366      1.1  christos 
    367      1.1  christos   fprintf (stream, _("\n\
    368  1.1.1.2  christos     no-aliases    Use canonical instruction forms.\n"));
    369  1.1.1.2  christos   fprintf (stream, _("\n\
    370      1.1  christos     numeric       Print numeric register names, rather than ABI names.\n"));
    371      1.1  christos   fprintf (stream, _("\n"));
    372      1.1  christos }
    373