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