1 /* LoongArch opcode support.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
3 Contributed by Loongson Ltd.
5 This file is part of the GNU opcodes library.
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)
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.
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/>. */
22 #include "disassemble.h"
24 #include "opcode/loongarch.h"
25 #include "libiberty.h"
28 static bool loongarch_dis_show_aliases
= true;
29 static const char *const *loongarch_r_disname
= NULL
;
30 static const char *const *loongarch_f_disname
= NULL
;
31 static const char *const *loongarch_fc_disname
= NULL
;
32 static const char *const *loongarch_c_disname
= NULL
;
33 static const char *const *loongarch_cr_disname
= NULL
;
34 static const char *const *loongarch_v_disname
= NULL
;
35 static const char *const *loongarch_x_disname
= NULL
;
37 static const struct loongarch_opcode
*
38 get_loongarch_opcode_by_binfmt (insn_t insn
)
40 const struct loongarch_opcode
*it
;
41 struct loongarch_ase
*ase
;
43 for (ase
= loongarch_ASEs
; ase
->enabled
; ase
++)
45 if (!*ase
->enabled
|| (ase
->include
&& !*ase
->include
)
46 || (ase
->exclude
&& *ase
->exclude
))
49 if (!ase
->opc_htab_inited
)
51 for (it
= ase
->opcodes
; it
->mask
; it
++)
52 if (!ase
->opc_htab
[LARCH_INSN_OPC (it
->match
)]
54 && (!(it
->pinfo
& INSN_DIS_ALIAS
)
55 || loongarch_dis_show_aliases
))
56 ase
->opc_htab
[LARCH_INSN_OPC (it
->match
)] = it
;
57 for (i
= 0; i
< 16; i
++)
58 if (!ase
->opc_htab
[i
])
59 ase
->opc_htab
[i
] = it
;
60 ase
->opc_htab_inited
= 1;
63 it
= ase
->opc_htab
[LARCH_INSN_OPC (insn
)];
64 for (; it
->name
; it
++)
65 if ((insn
& it
->mask
) == it
->match
&& it
->mask
66 && !(it
->include
&& !*it
->include
)
67 && !(it
->exclude
&& *it
->exclude
))
74 set_default_loongarch_dis_options (void)
76 LARCH_opts
.ase_ilp32
= 1;
77 LARCH_opts
.ase_lp64
= 1;
78 LARCH_opts
.ase_sf
= 1;
79 LARCH_opts
.ase_df
= 1;
80 LARCH_opts
.ase_lsx
= 1;
81 LARCH_opts
.ase_lasx
= 1;
82 LARCH_opts
.ase_lvz
= 1;
83 LARCH_opts
.ase_lbt
= 1;
85 loongarch_r_disname
= loongarch_r_alias
;
86 loongarch_f_disname
= loongarch_f_alias
;
87 loongarch_fc_disname
= loongarch_fc_normal_name
;
88 loongarch_c_disname
= loongarch_c_normal_name
;
89 loongarch_cr_disname
= loongarch_cr_normal_name
;
90 loongarch_v_disname
= loongarch_v_normal_name
;
91 loongarch_x_disname
= loongarch_x_normal_name
;
95 parse_loongarch_dis_option (const char *option
)
97 if (strcmp (option
, "no-aliases") == 0)
98 loongarch_dis_show_aliases
= false;
100 if (strcmp (option
, "numeric") == 0)
102 loongarch_r_disname
= loongarch_r_normal_name
;
103 loongarch_f_disname
= loongarch_f_normal_name
;
109 parse_loongarch_dis_options (const char *opts_in
)
111 set_default_loongarch_dis_options ();
116 char *opts
, *opt
, *opt_end
;
117 opts
= xmalloc (strlen (opts_in
) + 1);
118 strcpy (opts
, opts_in
);
120 for (opt
= opt_end
= opts
; opt_end
!= NULL
; opt
= opt_end
+ 1)
122 if ((opt_end
= strchr (opt
, ',')) != NULL
)
124 if (parse_loongarch_dis_option (opt
) != 0)
132 dis_one_arg (char esc1
, char esc2
, const char *bit_field
,
133 const char *arg ATTRIBUTE_UNUSED
, void *context
)
135 static int need_comma
= 0;
136 struct disassemble_info
*info
= context
;
137 insn_t insn
= *(insn_t
*) info
->private_data
;
139 enum disassembler_style style
;
144 info
->fprintf_styled_func (info
->stream
, dis_style_text
, ", ");
146 imm
= loongarch_decode_imm (bit_field
, insn
, 1);
147 u_imm
= loongarch_decode_imm (bit_field
, insn
, 0);
153 info
->fprintf_styled_func (info
->stream
, dis_style_register
, "%s", loongarch_r_disname
[u_imm
]);
159 info
->fprintf_styled_func (info
->stream
, dis_style_register
, "%s", loongarch_fc_disname
[u_imm
]);
162 info
->fprintf_styled_func (info
->stream
, dis_style_register
, "%s", loongarch_f_disname
[u_imm
]);
169 info
->fprintf_styled_func (info
->stream
, dis_style_register
, "%s", loongarch_cr_disname
[u_imm
]);
172 info
->fprintf_styled_func (info
->stream
, dis_style_register
, "%s", loongarch_c_disname
[u_imm
]);
176 info
->fprintf_styled_func (info
->stream
, dis_style_register
, "%s", loongarch_v_disname
[u_imm
]);
179 info
->fprintf_styled_func (info
->stream
, dis_style_register
, "%s", loongarch_x_disname
[u_imm
]);
182 style
= esc2
== 'o' ? dis_style_address_offset
: dis_style_immediate
;
183 info
->fprintf_styled_func (info
->stream
, style
, "0x%x", u_imm
);
190 /* Both represent address offsets. */
191 style
= dis_style_address_offset
;
194 style
= dis_style_immediate
;
197 info
->fprintf_styled_func (info
->stream
, style
, "%d", imm
);
201 info
->insn_type
= dis_branch
;
212 disassemble_one (insn_t insn
, struct disassemble_info
*info
)
214 const struct loongarch_opcode
*opc
= get_loongarch_opcode_by_binfmt (insn
);
216 #ifdef LOONGARCH_DEBUG
217 char have_space
[32] = { 0 };
220 const char *t_f
= opc
? opc
->format
: NULL
;
224 while (('a' <= t_f
[0] && t_f
[0] <= 'z')
225 || ('A' <= t_f
[0] && t_f
[0] <= 'Z')
230 i
= strtol (t_f
, &t_f
, 10);
233 i
+= strtol (t_f
, &t_f
, 10);
241 t_f
+= 2; /* '<' '<' */
242 strtol (t_f
, &t_f
, 10);
247 t
= ~((insn_t
) -1 >> 1);
248 for (i
= 31; 0 <= i
; i
--)
251 info
->fprintf_styled_func (info
->stream
, dis_style_text
, "1");
253 info
->fprintf_styled_func (info
->stream
, dis_style_text
, "0");
255 info
->fprintf_styled_func (info
->stream
, dis_style_text
, " ");
258 info
->fprintf_styled_func (info
->stream
, dis_style_text
, "\t");
263 info
->insn_type
= dis_noninsn
;
264 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
, ".word\t\t");
265 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
, "0x%08x", insn
);
269 info
->insn_type
= dis_nonbranch
;
270 if (opc
->format
== NULL
|| opc
->format
[0] == '\0')
271 info
->fprintf_styled_func (info
->stream
, dis_style_mnemonic
,
274 info
->fprintf_styled_func (info
->stream
, dis_style_mnemonic
,
278 char *fake_args
= xmalloc (strlen (opc
->format
) + 1);
279 const char *fake_arg_strs
[MAX_ARG_NUM_PLUS_2
];
280 strcpy (fake_args
, opc
->format
);
281 if (0 < loongarch_split_args_by_comma (fake_args
, fake_arg_strs
))
282 info
->fprintf_styled_func (info
->stream
, dis_style_text
, "\t");
283 info
->private_data
= &insn
;
284 loongarch_foreach_args (opc
->format
, fake_arg_strs
, dis_one_arg
, info
);
288 if (info
->insn_type
== dis_branch
|| info
->insn_type
== dis_condbranch
)
290 info
->fprintf_styled_func (info
->stream
, dis_style_comment_start
, "\t# ");
291 info
->print_address_func (info
->target
, info
);
296 print_insn_loongarch (bfd_vma memaddr
, struct disassemble_info
*info
)
301 static int not_init_yet
= 1;
304 parse_loongarch_dis_options (info
->disassembler_options
);
308 info
->bytes_per_chunk
= 4;
309 info
->bytes_per_line
= 4;
310 info
->display_endian
= BFD_ENDIAN_LITTLE
;
311 info
->insn_info_valid
= 1;
312 info
->target
= memaddr
;
314 if ((status
= info
->read_memory_func (memaddr
, (bfd_byte
*) &insn
,
315 sizeof (insn
), info
)) != 0)
317 info
->memory_error_func (status
, memaddr
, info
);
318 return -1; /* loongarch_insn_length (0); */
321 disassemble_one (insn
, info
);
323 return loongarch_insn_length (insn
);
327 print_loongarch_disassembler_options (FILE *stream
)
329 fprintf (stream
, _("\n\
330 The following LoongArch disassembler options are supported for use\n\
331 with the -M switch (multiple options should be separated by commas):\n"));
333 fprintf (stream
, _("\n\
334 no-aliases Use canonical instruction forms.\n"));
335 fprintf (stream
, _("\n\
336 numeric Print numeric register names, rather than ABI names.\n"));
337 fprintf (stream
, _("\n"));