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