Home | History | Annotate | Line # | Download | only in opcodes
      1      1.1  christos /* LoongArch opcode support.
      2  1.1.1.2  christos    Copyright (C) 2021-2024 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  christos 	  return it;
     69      1.1  christos     }
     70      1.1  christos   return NULL;
     71      1.1  christos }
     72      1.1  christos 
     73      1.1  christos static void
     74      1.1  christos set_default_loongarch_dis_options (void)
     75      1.1  christos {
     76      1.1  christos   LARCH_opts.ase_ilp32 = 1;
     77      1.1  christos   LARCH_opts.ase_lp64 = 1;
     78      1.1  christos   LARCH_opts.ase_sf = 1;
     79      1.1  christos   LARCH_opts.ase_df = 1;
     80      1.1  christos   LARCH_opts.ase_lsx = 1;
     81      1.1  christos   LARCH_opts.ase_lasx = 1;
     82  1.1.1.2  christos   LARCH_opts.ase_lvz = 1;
     83  1.1.1.2  christos   LARCH_opts.ase_lbt = 1;
     84      1.1  christos 
     85  1.1.1.2  christos   loongarch_r_disname = loongarch_r_alias;
     86  1.1.1.2  christos   loongarch_f_disname = loongarch_f_alias;
     87  1.1.1.2  christos   loongarch_fc_disname = loongarch_fc_normal_name;
     88      1.1  christos   loongarch_c_disname = loongarch_c_normal_name;
     89      1.1  christos   loongarch_cr_disname = loongarch_cr_normal_name;
     90      1.1  christos   loongarch_v_disname = loongarch_v_normal_name;
     91      1.1  christos   loongarch_x_disname = loongarch_x_normal_name;
     92      1.1  christos }
     93      1.1  christos 
     94      1.1  christos static int
     95      1.1  christos parse_loongarch_dis_option (const char *option)
     96      1.1  christos {
     97  1.1.1.2  christos   if (strcmp (option, "no-aliases") == 0)
     98  1.1.1.2  christos     loongarch_dis_show_aliases = false;
     99  1.1.1.2  christos 
    100      1.1  christos   if (strcmp (option, "numeric") == 0)
    101      1.1  christos     {
    102      1.1  christos       loongarch_r_disname = loongarch_r_normal_name;
    103      1.1  christos       loongarch_f_disname = loongarch_f_normal_name;
    104      1.1  christos     }
    105      1.1  christos   return -1;
    106      1.1  christos }
    107      1.1  christos 
    108      1.1  christos static int
    109      1.1  christos parse_loongarch_dis_options (const char *opts_in)
    110      1.1  christos {
    111      1.1  christos   set_default_loongarch_dis_options ();
    112      1.1  christos 
    113      1.1  christos   if (opts_in == NULL)
    114      1.1  christos     return 0;
    115      1.1  christos 
    116      1.1  christos   char *opts, *opt, *opt_end;
    117      1.1  christos   opts = xmalloc (strlen (opts_in) + 1);
    118      1.1  christos   strcpy (opts, opts_in);
    119      1.1  christos 
    120      1.1  christos   for (opt = opt_end = opts; opt_end != NULL; opt = opt_end + 1)
    121      1.1  christos     {
    122      1.1  christos       if ((opt_end = strchr (opt, ',')) != NULL)
    123      1.1  christos 	*opt_end = 0;
    124      1.1  christos       if (parse_loongarch_dis_option (opt) != 0)
    125      1.1  christos 	return -1;
    126      1.1  christos     }
    127      1.1  christos   free (opts);
    128      1.1  christos   return 0;
    129      1.1  christos }
    130      1.1  christos 
    131      1.1  christos static int32_t
    132      1.1  christos dis_one_arg (char esc1, char esc2, const char *bit_field,
    133      1.1  christos 	     const char *arg ATTRIBUTE_UNUSED, void *context)
    134      1.1  christos {
    135      1.1  christos   static int need_comma = 0;
    136      1.1  christos   struct disassemble_info *info = context;
    137      1.1  christos   insn_t insn = *(insn_t *) info->private_data;
    138      1.1  christos   int32_t imm, u_imm;
    139  1.1.1.2  christos   enum disassembler_style style;
    140      1.1  christos 
    141      1.1  christos   if (esc1)
    142      1.1  christos     {
    143      1.1  christos       if (need_comma)
    144  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, ", ");
    145      1.1  christos       need_comma = 1;
    146      1.1  christos       imm = loongarch_decode_imm (bit_field, insn, 1);
    147      1.1  christos       u_imm = loongarch_decode_imm (bit_field, insn, 0);
    148      1.1  christos     }
    149      1.1  christos 
    150      1.1  christos   switch (esc1)
    151      1.1  christos     {
    152      1.1  christos     case 'r':
    153  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_r_disname[u_imm]);
    154      1.1  christos       break;
    155      1.1  christos     case 'f':
    156  1.1.1.2  christos       switch (esc2)
    157  1.1.1.2  christos 	{
    158  1.1.1.2  christos 	case 'c':
    159  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_fc_disname[u_imm]);
    160  1.1.1.2  christos 	  break;
    161  1.1.1.2  christos 	default:
    162  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_f_disname[u_imm]);
    163  1.1.1.2  christos 	}
    164      1.1  christos       break;
    165      1.1  christos     case 'c':
    166      1.1  christos       switch (esc2)
    167      1.1  christos 	{
    168      1.1  christos 	case 'r':
    169  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_cr_disname[u_imm]);
    170      1.1  christos 	  break;
    171      1.1  christos 	default:
    172  1.1.1.2  christos 	  info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_c_disname[u_imm]);
    173      1.1  christos 	}
    174      1.1  christos       break;
    175      1.1  christos     case 'v':
    176  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_v_disname[u_imm]);
    177      1.1  christos       break;
    178      1.1  christos     case 'x':
    179  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_register, "%s", loongarch_x_disname[u_imm]);
    180      1.1  christos       break;
    181      1.1  christos     case 'u':
    182  1.1.1.2  christos       style = esc2 == 'o' ? dis_style_address_offset : dis_style_immediate;
    183  1.1.1.2  christos       info->fprintf_styled_func (info->stream, style, "0x%x", u_imm);
    184      1.1  christos       break;
    185      1.1  christos     case 's':
    186  1.1.1.2  christos       switch (esc2)
    187  1.1.1.2  christos 	{
    188  1.1.1.2  christos 	case 'b':
    189  1.1.1.2  christos 	case 'o':
    190  1.1.1.2  christos 	  /* Both represent address offsets.  */
    191  1.1.1.2  christos 	  style = dis_style_address_offset;
    192  1.1.1.2  christos 	  break;
    193  1.1.1.2  christos 	default:
    194  1.1.1.2  christos 	  style = dis_style_immediate;
    195  1.1.1.2  christos 	  break;
    196  1.1.1.2  christos 	}
    197  1.1.1.2  christos       info->fprintf_styled_func (info->stream, style, "%d", imm);
    198      1.1  christos       switch (esc2)
    199      1.1  christos 	{
    200      1.1  christos 	case 'b':
    201      1.1  christos 	  info->insn_type = dis_branch;
    202      1.1  christos 	  info->target += imm;
    203      1.1  christos 	}
    204      1.1  christos       break;
    205      1.1  christos     case '\0':
    206      1.1  christos       need_comma = 0;
    207      1.1  christos     }
    208      1.1  christos   return 0;
    209      1.1  christos }
    210      1.1  christos 
    211      1.1  christos static void
    212      1.1  christos disassemble_one (insn_t insn, struct disassemble_info *info)
    213      1.1  christos {
    214      1.1  christos   const struct loongarch_opcode *opc = get_loongarch_opcode_by_binfmt (insn);
    215      1.1  christos 
    216      1.1  christos #ifdef LOONGARCH_DEBUG
    217      1.1  christos   char have_space[32] = { 0 };
    218      1.1  christos   insn_t t;
    219      1.1  christos   int i;
    220      1.1  christos   const char *t_f = opc ? opc->format : NULL;
    221      1.1  christos   if (t_f)
    222      1.1  christos     while (*t_f)
    223      1.1  christos       {
    224      1.1  christos 	while (('a' <= t_f[0] && t_f[0] <= 'z')
    225      1.1  christos 	       || ('A' <= t_f[0] && t_f[0] <= 'Z')
    226      1.1  christos 	       || t_f[0] == ',')
    227      1.1  christos 	  t_f++;
    228      1.1  christos 	while (1)
    229      1.1  christos 	  {
    230      1.1  christos 	    i = strtol (t_f, &t_f, 10);
    231      1.1  christos 	    have_space[i] = 1;
    232      1.1  christos 	    t_f++; /* ':' */
    233      1.1  christos 	    i += strtol (t_f, &t_f, 10);
    234      1.1  christos 	    have_space[i] = 1;
    235      1.1  christos 	    if (t_f[0] == '|')
    236      1.1  christos 	      t_f++;
    237      1.1  christos 	    else
    238      1.1  christos 	      break;
    239      1.1  christos 	  }
    240      1.1  christos 	if (t_f[0] == '<')
    241      1.1  christos 	  t_f += 2; /* '<' '<' */
    242      1.1  christos 	strtol (t_f, &t_f, 10);
    243      1.1  christos       }
    244      1.1  christos 
    245      1.1  christos   have_space[28] = 1;
    246      1.1  christos   have_space[0] = 0;
    247      1.1  christos   t = ~((insn_t) -1 >> 1);
    248      1.1  christos   for (i = 31; 0 <= i; i--)
    249      1.1  christos     {
    250      1.1  christos       if (t & insn)
    251  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, "1");
    252      1.1  christos       else
    253  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, "0");
    254      1.1  christos       if (have_space[i])
    255  1.1.1.2  christos 	info->fprintf_styled_func (info->stream, dis_style_text, " ");
    256      1.1  christos       t = t >> 1;
    257      1.1  christos     }
    258  1.1.1.2  christos   info->fprintf_styled_func (info->stream, dis_style_text, "\t");
    259      1.1  christos #endif
    260      1.1  christos 
    261      1.1  christos   if (!opc)
    262      1.1  christos     {
    263      1.1  christos       info->insn_type = dis_noninsn;
    264  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_assembler_directive, ".word\t\t");
    265  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_immediate, "0x%08x", insn);
    266      1.1  christos       return;
    267      1.1  christos     }
    268      1.1  christos 
    269      1.1  christos   info->insn_type = dis_nonbranch;
    270  1.1.1.2  christos   if (opc->format == NULL || opc->format[0] == '\0')
    271  1.1.1.2  christos     info->fprintf_styled_func (info->stream, dis_style_mnemonic,
    272  1.1.1.2  christos 				"%s", opc->name);
    273  1.1.1.2  christos   else
    274  1.1.1.2  christos     info->fprintf_styled_func (info->stream, dis_style_mnemonic,
    275  1.1.1.2  christos 				"%-12s", opc->name);
    276      1.1  christos 
    277      1.1  christos   {
    278      1.1  christos     char *fake_args = xmalloc (strlen (opc->format) + 1);
    279      1.1  christos     const char *fake_arg_strs[MAX_ARG_NUM_PLUS_2];
    280      1.1  christos     strcpy (fake_args, opc->format);
    281      1.1  christos     if (0 < loongarch_split_args_by_comma (fake_args, fake_arg_strs))
    282  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_text, "\t");
    283      1.1  christos     info->private_data = &insn;
    284      1.1  christos     loongarch_foreach_args (opc->format, fake_arg_strs, dis_one_arg, info);
    285      1.1  christos     free (fake_args);
    286      1.1  christos   }
    287      1.1  christos 
    288      1.1  christos   if (info->insn_type == dis_branch || info->insn_type == dis_condbranch)
    289      1.1  christos     {
    290  1.1.1.2  christos       info->fprintf_styled_func (info->stream, dis_style_comment_start, "\t# ");
    291      1.1  christos       info->print_address_func (info->target, info);
    292      1.1  christos     }
    293      1.1  christos }
    294      1.1  christos 
    295      1.1  christos int
    296      1.1  christos print_insn_loongarch (bfd_vma memaddr, struct disassemble_info *info)
    297      1.1  christos {
    298      1.1  christos   insn_t insn;
    299      1.1  christos   int status;
    300      1.1  christos 
    301      1.1  christos   static int not_init_yet = 1;
    302      1.1  christos   if (not_init_yet)
    303      1.1  christos     {
    304      1.1  christos       parse_loongarch_dis_options (info->disassembler_options);
    305      1.1  christos       not_init_yet = 0;
    306      1.1  christos     }
    307      1.1  christos 
    308      1.1  christos   info->bytes_per_chunk = 4;
    309      1.1  christos   info->bytes_per_line = 4;
    310      1.1  christos   info->display_endian = BFD_ENDIAN_LITTLE;
    311      1.1  christos   info->insn_info_valid = 1;
    312      1.1  christos   info->target = memaddr;
    313      1.1  christos 
    314      1.1  christos   if ((status = info->read_memory_func (memaddr, (bfd_byte *) &insn,
    315      1.1  christos 					sizeof (insn), info)) != 0)
    316      1.1  christos     {
    317      1.1  christos       info->memory_error_func (status, memaddr, info);
    318      1.1  christos       return -1; /* loongarch_insn_length (0); */
    319      1.1  christos     }
    320      1.1  christos 
    321      1.1  christos   disassemble_one (insn, info);
    322      1.1  christos 
    323      1.1  christos   return loongarch_insn_length (insn);
    324      1.1  christos }
    325      1.1  christos 
    326      1.1  christos void
    327      1.1  christos print_loongarch_disassembler_options (FILE *stream)
    328      1.1  christos {
    329      1.1  christos   fprintf (stream, _("\n\
    330      1.1  christos The following LoongArch disassembler options are supported for use\n\
    331      1.1  christos with the -M switch (multiple options should be separated by commas):\n"));
    332      1.1  christos 
    333      1.1  christos   fprintf (stream, _("\n\
    334  1.1.1.2  christos     no-aliases    Use canonical instruction forms.\n"));
    335  1.1.1.2  christos   fprintf (stream, _("\n\
    336      1.1  christos     numeric       Print numeric register names, rather than ABI names.\n"));
    337      1.1  christos   fprintf (stream, _("\n"));
    338      1.1  christos }
    339