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