]>
Commit | Line | Data |
---|---|---|
66d29a5e | 1 | /* |
e2be9a5c | 2 | * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. |
66d29a5e TS |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include "qemu/osdep.h" | |
66d29a5e TS |
19 | #include "iclass.h" |
20 | #include "attribs.h" | |
21 | #include "genptr.h" | |
22 | #include "decode.h" | |
23 | #include "insn.h" | |
24 | #include "printinsn.h" | |
60d1180b | 25 | #include "mmvec/decode_ext_mmvec.h" |
66d29a5e TS |
26 | |
27 | #define fZXTN(N, M, VAL) ((VAL) & ((1LL << (N)) - 1)) | |
28 | ||
29 | enum { | |
30 | EXT_IDX_noext = 0, | |
31 | EXT_IDX_noext_AFTER = 4, | |
32 | EXT_IDX_mmvec = 4, | |
33 | EXT_IDX_mmvec_AFTER = 8, | |
34 | XX_LAST_EXT_IDX | |
35 | }; | |
36 | ||
37 | /* | |
38 | * Certain operand types represent a non-contiguous set of values. | |
39 | * For example, the compound compare-and-jump instruction can only access | |
40 | * registers R0-R7 and R16-23. | |
41 | * This table represents the mapping from the encoding to the actual values. | |
42 | */ | |
43 | ||
44 | #define DEF_REGMAP(NAME, ELEMENTS, ...) \ | |
45 | static const unsigned int DECODE_REGISTER_##NAME[ELEMENTS] = \ | |
46 | { __VA_ARGS__ }; | |
47 | /* Name Num Table */ | |
48 | DEF_REGMAP(R_16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23) | |
49 | DEF_REGMAP(R__8, 8, 0, 2, 4, 6, 16, 18, 20, 22) | |
61c9aab0 | 50 | DEF_REGMAP(R_8, 8, 0, 1, 2, 3, 4, 5, 6, 7) |
66d29a5e | 51 | |
1de468b3 TS |
52 | #define DECODE_MAPPED_REG(OPNUM, NAME) \ |
53 | insn->regno[OPNUM] = DECODE_REGISTER_##NAME[insn->regno[OPNUM]]; | |
66d29a5e TS |
54 | |
55 | typedef struct { | |
56 | const struct DectreeTable *table_link; | |
57 | const struct DectreeTable *table_link_b; | |
58 | Opcode opcode; | |
59 | enum { | |
60 | DECTREE_ENTRY_INVALID, | |
61 | DECTREE_TABLE_LINK, | |
62 | DECTREE_SUBINSNS, | |
63 | DECTREE_EXTSPACE, | |
64 | DECTREE_TERMINAL | |
65 | } type; | |
66 | } DectreeEntry; | |
67 | ||
68 | typedef struct DectreeTable { | |
69 | unsigned int (*lookup_function)(int startbit, int width, uint32_t opcode); | |
70 | unsigned int size; | |
71 | unsigned int startbit; | |
72 | unsigned int width; | |
73 | const DectreeEntry table[]; | |
74 | } DectreeTable; | |
75 | ||
76 | #define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) \ | |
77 | static const DectreeTable dectree_table_##TAG; | |
78 | #define TABLE_LINK(TABLE) /* NOTHING */ | |
79 | #define TERMINAL(TAG, ENC) /* NOTHING */ | |
80 | #define SUBINSNS(TAG, CLASSA, CLASSB, ENC) /* NOTHING */ | |
81 | #define EXTSPACE(TAG, ENC) /* NOTHING */ | |
82 | #define INVALID() /* NOTHING */ | |
83 | #define DECODE_END_TABLE(...) /* NOTHING */ | |
84 | #define DECODE_MATCH_INFO(...) /* NOTHING */ | |
85 | #define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ | |
86 | #define DECODE_OPINFO(...) /* NOTHING */ | |
87 | ||
88 | #include "dectree_generated.h.inc" | |
89 | ||
90 | #undef DECODE_OPINFO | |
91 | #undef DECODE_MATCH_INFO | |
92 | #undef DECODE_LEGACY_MATCH_INFO | |
93 | #undef DECODE_END_TABLE | |
94 | #undef INVALID | |
95 | #undef TERMINAL | |
96 | #undef SUBINSNS | |
97 | #undef EXTSPACE | |
98 | #undef TABLE_LINK | |
99 | #undef DECODE_NEW_TABLE | |
100 | #undef DECODE_SEPARATOR_BITS | |
101 | ||
102 | #define DECODE_SEPARATOR_BITS(START, WIDTH) NULL, START, WIDTH | |
103 | #define DECODE_NEW_TABLE_HELPER(TAG, SIZE, FN, START, WIDTH) \ | |
104 | static const DectreeTable dectree_table_##TAG = { \ | |
105 | .size = SIZE, \ | |
106 | .lookup_function = FN, \ | |
107 | .startbit = START, \ | |
108 | .width = WIDTH, \ | |
109 | .table = { | |
110 | #define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) \ | |
111 | DECODE_NEW_TABLE_HELPER(TAG, SIZE, WHATNOT) | |
112 | ||
113 | #define TABLE_LINK(TABLE) \ | |
114 | { .type = DECTREE_TABLE_LINK, .table_link = &dectree_table_##TABLE }, | |
115 | #define TERMINAL(TAG, ENC) \ | |
116 | { .type = DECTREE_TERMINAL, .opcode = TAG }, | |
117 | #define SUBINSNS(TAG, CLASSA, CLASSB, ENC) \ | |
118 | { \ | |
119 | .type = DECTREE_SUBINSNS, \ | |
120 | .table_link = &dectree_table_DECODE_SUBINSN_##CLASSA, \ | |
121 | .table_link_b = &dectree_table_DECODE_SUBINSN_##CLASSB \ | |
122 | }, | |
123 | #define EXTSPACE(TAG, ENC) { .type = DECTREE_EXTSPACE }, | |
124 | #define INVALID() { .type = DECTREE_ENTRY_INVALID, .opcode = XX_LAST_OPCODE }, | |
125 | ||
126 | #define DECODE_END_TABLE(...) } }; | |
127 | ||
128 | #define DECODE_MATCH_INFO(...) /* NOTHING */ | |
129 | #define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ | |
130 | #define DECODE_OPINFO(...) /* NOTHING */ | |
131 | ||
132 | #include "dectree_generated.h.inc" | |
133 | ||
134 | #undef DECODE_OPINFO | |
135 | #undef DECODE_MATCH_INFO | |
136 | #undef DECODE_LEGACY_MATCH_INFO | |
137 | #undef DECODE_END_TABLE | |
138 | #undef INVALID | |
139 | #undef TERMINAL | |
140 | #undef SUBINSNS | |
141 | #undef EXTSPACE | |
142 | #undef TABLE_LINK | |
143 | #undef DECODE_NEW_TABLE | |
144 | #undef DECODE_NEW_TABLE_HELPER | |
145 | #undef DECODE_SEPARATOR_BITS | |
146 | ||
147 | static const DectreeTable dectree_table_DECODE_EXT_EXT_noext = { | |
148 | .size = 1, .lookup_function = NULL, .startbit = 0, .width = 0, | |
149 | .table = { | |
150 | { .type = DECTREE_ENTRY_INVALID, .opcode = XX_LAST_OPCODE }, | |
151 | } | |
152 | }; | |
153 | ||
154 | static const DectreeTable *ext_trees[XX_LAST_EXT_IDX]; | |
155 | ||
156 | static void decode_ext_init(void) | |
157 | { | |
158 | int i; | |
159 | for (i = EXT_IDX_noext; i < EXT_IDX_noext_AFTER; i++) { | |
160 | ext_trees[i] = &dectree_table_DECODE_EXT_EXT_noext; | |
161 | } | |
61c9aab0 TS |
162 | for (i = EXT_IDX_mmvec; i < EXT_IDX_mmvec_AFTER; i++) { |
163 | ext_trees[i] = &dectree_table_DECODE_EXT_EXT_mmvec; | |
164 | } | |
66d29a5e TS |
165 | } |
166 | ||
167 | typedef struct { | |
168 | uint32_t mask; | |
169 | uint32_t match; | |
170 | } DecodeITableEntry; | |
171 | ||
172 | #define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) /* NOTHING */ | |
173 | #define TABLE_LINK(TABLE) /* NOTHING */ | |
174 | #define TERMINAL(TAG, ENC) /* NOTHING */ | |
175 | #define SUBINSNS(TAG, CLASSA, CLASSB, ENC) /* NOTHING */ | |
176 | #define EXTSPACE(TAG, ENC) /* NOTHING */ | |
177 | #define INVALID() /* NOTHING */ | |
178 | #define DECODE_END_TABLE(...) /* NOTHING */ | |
179 | #define DECODE_OPINFO(...) /* NOTHING */ | |
180 | ||
181 | #define DECODE_MATCH_INFO_NORMAL(TAG, MASK, MATCH) \ | |
182 | [TAG] = { \ | |
183 | .mask = MASK, \ | |
184 | .match = MATCH, \ | |
185 | }, | |
186 | ||
187 | #define DECODE_MATCH_INFO_NULL(TAG, MASK, MATCH) \ | |
188 | [TAG] = { .match = ~0 }, | |
189 | ||
190 | #define DECODE_MATCH_INFO(...) DECODE_MATCH_INFO_NORMAL(__VA_ARGS__) | |
191 | #define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ | |
192 | ||
193 | static const DecodeITableEntry decode_itable[XX_LAST_OPCODE] = { | |
194 | #include "dectree_generated.h.inc" | |
195 | }; | |
196 | ||
197 | #undef DECODE_MATCH_INFO | |
198 | #define DECODE_MATCH_INFO(...) DECODE_MATCH_INFO_NULL(__VA_ARGS__) | |
199 | ||
200 | #undef DECODE_LEGACY_MATCH_INFO | |
201 | #define DECODE_LEGACY_MATCH_INFO(...) DECODE_MATCH_INFO_NORMAL(__VA_ARGS__) | |
202 | ||
203 | static const DecodeITableEntry decode_legacy_itable[XX_LAST_OPCODE] = { | |
204 | #include "dectree_generated.h.inc" | |
205 | }; | |
206 | ||
207 | #undef DECODE_OPINFO | |
208 | #undef DECODE_MATCH_INFO | |
209 | #undef DECODE_LEGACY_MATCH_INFO | |
210 | #undef DECODE_END_TABLE | |
211 | #undef INVALID | |
212 | #undef TERMINAL | |
213 | #undef SUBINSNS | |
214 | #undef EXTSPACE | |
215 | #undef TABLE_LINK | |
216 | #undef DECODE_NEW_TABLE | |
217 | #undef DECODE_SEPARATOR_BITS | |
218 | ||
219 | void decode_init(void) | |
220 | { | |
221 | decode_ext_init(); | |
222 | } | |
223 | ||
224 | void decode_send_insn_to(Packet *packet, int start, int newloc) | |
225 | { | |
226 | Insn tmpinsn; | |
227 | int direction; | |
228 | int i; | |
229 | if (start == newloc) { | |
230 | return; | |
231 | } | |
232 | if (start < newloc) { | |
233 | /* Move towards end */ | |
234 | direction = 1; | |
235 | } else { | |
236 | /* move towards beginning */ | |
237 | direction = -1; | |
238 | } | |
239 | for (i = start; i != newloc; i += direction) { | |
240 | tmpinsn = packet->insn[i]; | |
241 | packet->insn[i] = packet->insn[i + direction]; | |
242 | packet->insn[i + direction] = tmpinsn; | |
243 | } | |
244 | } | |
245 | ||
246 | /* Fill newvalue registers with the correct regno */ | |
247 | static void | |
248 | decode_fill_newvalue_regno(Packet *packet) | |
249 | { | |
250 | int i, use_regidx, offset, def_idx, dst_idx; | |
251 | uint16_t def_opcode, use_opcode; | |
252 | char *dststr; | |
253 | ||
254 | for (i = 1; i < packet->num_insns; i++) { | |
255 | if (GET_ATTRIB(packet->insn[i].opcode, A_DOTNEWVALUE) && | |
256 | !GET_ATTRIB(packet->insn[i].opcode, A_EXTENSION)) { | |
257 | use_opcode = packet->insn[i].opcode; | |
258 | ||
259 | /* It's a store, so we're adjusting the Nt field */ | |
260 | if (GET_ATTRIB(use_opcode, A_STORE)) { | |
261 | use_regidx = strchr(opcode_reginfo[use_opcode], 't') - | |
262 | opcode_reginfo[use_opcode]; | |
263 | } else { /* It's a Jump, so we're adjusting the Ns field */ | |
264 | use_regidx = strchr(opcode_reginfo[use_opcode], 's') - | |
265 | opcode_reginfo[use_opcode]; | |
266 | } | |
267 | ||
268 | /* | |
269 | * What's encoded at the N-field is the offset to who's producing | |
270 | * the value. Shift off the LSB which indicates odd/even register, | |
271 | * then walk backwards and skip over the constant extenders. | |
272 | */ | |
273 | offset = packet->insn[i].regno[use_regidx] >> 1; | |
274 | def_idx = i - offset; | |
275 | for (int j = 0; j < offset; j++) { | |
276 | if (GET_ATTRIB(packet->insn[i - j - 1].opcode, A_IT_EXTENDER)) { | |
277 | def_idx--; | |
278 | } | |
279 | } | |
280 | ||
281 | /* | |
282 | * Check for a badly encoded N-field which points to an instruction | |
283 | * out-of-range | |
284 | */ | |
285 | g_assert(!((def_idx < 0) || (def_idx > (packet->num_insns - 1)))); | |
286 | ||
287 | /* | |
288 | * packet->insn[def_idx] is the producer | |
289 | * Figure out which type of destination it produces | |
290 | * and the corresponding index in the reginfo | |
291 | */ | |
292 | def_opcode = packet->insn[def_idx].opcode; | |
293 | dststr = strstr(opcode_wregs[def_opcode], "Rd"); | |
294 | if (dststr) { | |
295 | dststr = strchr(opcode_reginfo[def_opcode], 'd'); | |
296 | } else { | |
297 | dststr = strstr(opcode_wregs[def_opcode], "Rx"); | |
298 | if (dststr) { | |
299 | dststr = strchr(opcode_reginfo[def_opcode], 'x'); | |
300 | } else { | |
301 | dststr = strstr(opcode_wregs[def_opcode], "Re"); | |
302 | if (dststr) { | |
303 | dststr = strchr(opcode_reginfo[def_opcode], 'e'); | |
304 | } else { | |
305 | dststr = strstr(opcode_wregs[def_opcode], "Ry"); | |
306 | if (dststr) { | |
307 | dststr = strchr(opcode_reginfo[def_opcode], 'y'); | |
308 | } else { | |
309 | g_assert_not_reached(); | |
310 | } | |
311 | } | |
312 | } | |
313 | } | |
314 | g_assert(dststr != NULL); | |
315 | ||
316 | /* Now patch up the consumer with the register number */ | |
317 | dst_idx = dststr - opcode_reginfo[def_opcode]; | |
318 | packet->insn[i].regno[use_regidx] = | |
319 | packet->insn[def_idx].regno[dst_idx]; | |
320 | /* | |
321 | * We need to remember who produces this value to later | |
322 | * check if it was dynamically cancelled | |
323 | */ | |
324 | packet->insn[i].new_value_producer_slot = | |
325 | packet->insn[def_idx].slot; | |
326 | } | |
327 | } | |
328 | } | |
329 | ||
330 | /* Split CJ into a compare and a jump */ | |
331 | static void decode_split_cmpjump(Packet *pkt) | |
332 | { | |
333 | int last, i; | |
334 | int numinsns = pkt->num_insns; | |
335 | ||
336 | /* | |
337 | * First, split all compare-jumps. | |
338 | * The compare is sent to the end as a new instruction. | |
339 | * Do it this way so we don't reorder dual jumps. Those need to stay in | |
340 | * original order. | |
341 | */ | |
342 | for (i = 0; i < numinsns; i++) { | |
343 | /* It's a cmp-jump */ | |
344 | if (GET_ATTRIB(pkt->insn[i].opcode, A_NEWCMPJUMP)) { | |
345 | last = pkt->num_insns; | |
346 | pkt->insn[last] = pkt->insn[i]; /* copy the instruction */ | |
92cfa25f TS |
347 | pkt->insn[last].part1 = true; /* last insn does the CMP */ |
348 | pkt->insn[i].part1 = false; /* existing insn does the JUMP */ | |
66d29a5e TS |
349 | pkt->num_insns++; |
350 | } | |
351 | } | |
352 | ||
353 | /* Now re-shuffle all the compares back to the beginning */ | |
354 | for (i = 0; i < pkt->num_insns; i++) { | |
355 | if (pkt->insn[i].part1) { | |
356 | decode_send_insn_to(pkt, i, 0); | |
357 | } | |
358 | } | |
359 | } | |
360 | ||
92cfa25f | 361 | static bool decode_opcode_can_jump(int opcode) |
66d29a5e TS |
362 | { |
363 | if ((GET_ATTRIB(opcode, A_JUMP)) || | |
364 | (GET_ATTRIB(opcode, A_CALL)) || | |
365 | (opcode == J2_trap0) || | |
366 | (opcode == J2_pause)) { | |
367 | /* Exception to A_JUMP attribute */ | |
368 | if (opcode == J4_hintjumpr) { | |
92cfa25f | 369 | return false; |
66d29a5e | 370 | } |
92cfa25f | 371 | return true; |
66d29a5e TS |
372 | } |
373 | ||
92cfa25f | 374 | return false; |
66d29a5e TS |
375 | } |
376 | ||
92cfa25f | 377 | static bool decode_opcode_ends_loop(int opcode) |
66d29a5e TS |
378 | { |
379 | return GET_ATTRIB(opcode, A_HWLOOP0_END) || | |
380 | GET_ATTRIB(opcode, A_HWLOOP1_END); | |
381 | } | |
382 | ||
383 | /* Set the is_* fields in each instruction */ | |
384 | static void decode_set_insn_attr_fields(Packet *pkt) | |
385 | { | |
386 | int i; | |
387 | int numinsns = pkt->num_insns; | |
388 | uint16_t opcode; | |
389 | ||
92cfa25f | 390 | pkt->pkt_has_cof = false; |
fb67c2bf | 391 | pkt->pkt_has_multi_cof = false; |
92cfa25f TS |
392 | pkt->pkt_has_endloop = false; |
393 | pkt->pkt_has_dczeroa = false; | |
66d29a5e TS |
394 | |
395 | for (i = 0; i < numinsns; i++) { | |
396 | opcode = pkt->insn[i].opcode; | |
397 | if (pkt->insn[i].part1) { | |
398 | continue; /* Skip compare of cmp-jumps */ | |
399 | } | |
400 | ||
401 | if (GET_ATTRIB(opcode, A_DCZEROA)) { | |
92cfa25f | 402 | pkt->pkt_has_dczeroa = true; |
66d29a5e TS |
403 | } |
404 | ||
405 | if (GET_ATTRIB(opcode, A_STORE)) { | |
e2be9a5c TS |
406 | if (GET_ATTRIB(opcode, A_SCALAR_STORE) && |
407 | !GET_ATTRIB(opcode, A_MEMSIZE_0B)) { | |
408 | if (pkt->insn[i].slot == 0) { | |
409 | pkt->pkt_has_store_s0 = true; | |
410 | } else { | |
411 | pkt->pkt_has_store_s1 = true; | |
412 | } | |
66d29a5e TS |
413 | } |
414 | } | |
415 | ||
fb67c2bf TS |
416 | if (decode_opcode_can_jump(opcode)) { |
417 | if (pkt->pkt_has_cof) { | |
418 | pkt->pkt_has_multi_cof = true; | |
419 | } | |
420 | pkt->pkt_has_cof = true; | |
421 | } | |
66d29a5e TS |
422 | |
423 | pkt->insn[i].is_endloop = decode_opcode_ends_loop(opcode); | |
424 | ||
425 | pkt->pkt_has_endloop |= pkt->insn[i].is_endloop; | |
426 | ||
fb67c2bf TS |
427 | if (pkt->pkt_has_endloop) { |
428 | if (pkt->pkt_has_cof) { | |
429 | pkt->pkt_has_multi_cof = true; | |
430 | } | |
431 | pkt->pkt_has_cof = true; | |
432 | } | |
66d29a5e TS |
433 | } |
434 | } | |
435 | ||
436 | /* | |
437 | * Shuffle for execution | |
438 | * Move stores to end (in same order as encoding) | |
439 | * Move compares to beginning (for use by .new insns) | |
440 | */ | |
441 | static void decode_shuffle_for_execution(Packet *packet) | |
442 | { | |
92cfa25f | 443 | bool changed = false; |
66d29a5e | 444 | int i; |
92cfa25f | 445 | bool flag; /* flag means we've seen a non-memory instruction */ |
66d29a5e TS |
446 | int n_mems; |
447 | int last_insn = packet->num_insns - 1; | |
448 | ||
449 | /* | |
450 | * Skip end loops, somehow an end loop is getting in and messing | |
451 | * up the order | |
452 | */ | |
453 | if (decode_opcode_ends_loop(packet->insn[last_insn].opcode)) { | |
454 | last_insn--; | |
455 | } | |
456 | ||
457 | do { | |
92cfa25f | 458 | changed = false; |
66d29a5e TS |
459 | /* |
460 | * Stores go last, must not reorder. | |
461 | * Cannot shuffle stores past loads, either. | |
462 | * Iterate backwards. If we see a non-memory instruction, | |
463 | * then a store, shuffle the store to the front. Don't shuffle | |
464 | * stores wrt each other or a load. | |
465 | */ | |
92cfa25f | 466 | for (flag = false, n_mems = 0, i = last_insn; i >= 0; i--) { |
66d29a5e TS |
467 | int opcode = packet->insn[i].opcode; |
468 | ||
469 | if (flag && GET_ATTRIB(opcode, A_STORE)) { | |
470 | decode_send_insn_to(packet, i, last_insn - n_mems); | |
471 | n_mems++; | |
92cfa25f | 472 | changed = true; |
66d29a5e TS |
473 | } else if (GET_ATTRIB(opcode, A_STORE)) { |
474 | n_mems++; | |
475 | } else if (GET_ATTRIB(opcode, A_LOAD)) { | |
476 | /* | |
477 | * Don't set flag, since we don't want to shuffle a | |
478 | * store past a load | |
479 | */ | |
480 | n_mems++; | |
481 | } else if (GET_ATTRIB(opcode, A_DOTNEWVALUE)) { | |
482 | /* | |
483 | * Don't set flag, since we don't want to shuffle past | |
484 | * a .new value | |
485 | */ | |
486 | } else { | |
92cfa25f | 487 | flag = true; |
66d29a5e TS |
488 | } |
489 | } | |
490 | ||
491 | if (changed) { | |
492 | continue; | |
493 | } | |
494 | /* Compares go first, may be reordered wrt each other */ | |
92cfa25f | 495 | for (flag = false, i = 0; i < last_insn + 1; i++) { |
66d29a5e TS |
496 | int opcode = packet->insn[i].opcode; |
497 | ||
498 | if ((strstr(opcode_wregs[opcode], "Pd4") || | |
499 | strstr(opcode_wregs[opcode], "Pe4")) && | |
500 | GET_ATTRIB(opcode, A_STORE) == 0) { | |
501 | /* This should be a compare (not a store conditional) */ | |
502 | if (flag) { | |
503 | decode_send_insn_to(packet, i, 0); | |
92cfa25f | 504 | changed = true; |
66d29a5e TS |
505 | continue; |
506 | } | |
507 | } else if (GET_ATTRIB(opcode, A_IMPLICIT_WRITES_P3) && | |
508 | !decode_opcode_ends_loop(packet->insn[i].opcode)) { | |
509 | /* | |
510 | * spNloop instruction | |
511 | * Don't reorder endloops; they are not valid for .new uses, | |
512 | * and we want to match HW | |
513 | */ | |
514 | if (flag) { | |
515 | decode_send_insn_to(packet, i, 0); | |
92cfa25f | 516 | changed = true; |
66d29a5e TS |
517 | continue; |
518 | } | |
519 | } else if (GET_ATTRIB(opcode, A_IMPLICIT_WRITES_P0) && | |
520 | !GET_ATTRIB(opcode, A_NEWCMPJUMP)) { | |
521 | if (flag) { | |
522 | decode_send_insn_to(packet, i, 0); | |
92cfa25f | 523 | changed = true; |
66d29a5e TS |
524 | continue; |
525 | } | |
526 | } else { | |
92cfa25f | 527 | flag = true; |
66d29a5e TS |
528 | } |
529 | } | |
530 | if (changed) { | |
531 | continue; | |
532 | } | |
533 | } while (changed); | |
534 | ||
535 | /* | |
536 | * If we have a .new register compare/branch, move that to the very | |
537 | * very end, past stores | |
538 | */ | |
539 | for (i = 0; i < last_insn; i++) { | |
540 | if (GET_ATTRIB(packet->insn[i].opcode, A_DOTNEWVALUE)) { | |
541 | decode_send_insn_to(packet, i, last_insn); | |
542 | break; | |
543 | } | |
544 | } | |
545 | } | |
546 | ||
547 | static void | |
548 | apply_extender(Packet *pkt, int i, uint32_t extender) | |
549 | { | |
550 | int immed_num; | |
551 | uint32_t base_immed; | |
552 | ||
553 | immed_num = opcode_which_immediate_is_extended(pkt->insn[i].opcode); | |
554 | base_immed = pkt->insn[i].immed[immed_num]; | |
555 | ||
556 | pkt->insn[i].immed[immed_num] = extender | fZXTN(6, 32, base_immed); | |
557 | } | |
558 | ||
559 | static void decode_apply_extenders(Packet *packet) | |
560 | { | |
561 | int i; | |
562 | for (i = 0; i < packet->num_insns; i++) { | |
563 | if (GET_ATTRIB(packet->insn[i].opcode, A_IT_EXTENDER)) { | |
92cfa25f | 564 | packet->insn[i + 1].extension_valid = true; |
66d29a5e TS |
565 | apply_extender(packet, i + 1, packet->insn[i].immed[0]); |
566 | } | |
567 | } | |
568 | } | |
569 | ||
570 | static void decode_remove_extenders(Packet *packet) | |
571 | { | |
572 | int i, j; | |
573 | for (i = 0; i < packet->num_insns; i++) { | |
574 | if (GET_ATTRIB(packet->insn[i].opcode, A_IT_EXTENDER)) { | |
575 | /* Remove this one by moving the remaining instructions down */ | |
576 | for (j = i; | |
577 | (j < packet->num_insns - 1) && (j < INSTRUCTIONS_MAX - 1); | |
578 | j++) { | |
579 | packet->insn[j] = packet->insn[j + 1]; | |
580 | } | |
581 | packet->num_insns--; | |
582 | } | |
583 | } | |
584 | } | |
585 | ||
586 | static SlotMask get_valid_slots(const Packet *pkt, unsigned int slot) | |
587 | { | |
60d1180b TS |
588 | if (GET_ATTRIB(pkt->insn[slot].opcode, A_EXTENSION)) { |
589 | return mmvec_ext_decode_find_iclass_slots(pkt->insn[slot].opcode); | |
590 | } else { | |
591 | return find_iclass_slots(pkt->insn[slot].opcode, | |
592 | pkt->insn[slot].iclass); | |
593 | } | |
66d29a5e TS |
594 | } |
595 | ||
596 | #define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) /* NOTHING */ | |
597 | #define TABLE_LINK(TABLE) /* NOTHING */ | |
598 | #define TERMINAL(TAG, ENC) /* NOTHING */ | |
599 | #define SUBINSNS(TAG, CLASSA, CLASSB, ENC) /* NOTHING */ | |
600 | #define EXTSPACE(TAG, ENC) /* NOTHING */ | |
601 | #define INVALID() /* NOTHING */ | |
602 | #define DECODE_END_TABLE(...) /* NOTHING */ | |
603 | #define DECODE_MATCH_INFO(...) /* NOTHING */ | |
604 | #define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ | |
605 | ||
606 | #define DECODE_REG(REGNO, WIDTH, STARTBIT) \ | |
607 | insn->regno[REGNO] = ((encoding >> STARTBIT) & ((1 << WIDTH) - 1)); | |
608 | ||
609 | #define DECODE_IMPL_REG(REGNO, VAL) \ | |
610 | insn->regno[REGNO] = VAL; | |
611 | ||
612 | #define DECODE_IMM(IMMNO, WIDTH, STARTBIT, VALSTART) \ | |
613 | insn->immed[IMMNO] |= (((encoding >> STARTBIT) & ((1 << WIDTH) - 1))) << \ | |
614 | (VALSTART); | |
615 | ||
616 | #define DECODE_IMM_SXT(IMMNO, WIDTH) \ | |
617 | insn->immed[IMMNO] = ((((int32_t)insn->immed[IMMNO]) << (32 - WIDTH)) >> \ | |
618 | (32 - WIDTH)); | |
619 | ||
620 | #define DECODE_IMM_NEG(IMMNO, WIDTH) \ | |
621 | insn->immed[IMMNO] = -insn->immed[IMMNO]; | |
622 | ||
623 | #define DECODE_IMM_SHIFT(IMMNO, SHAMT) \ | |
624 | if ((!insn->extension_valid) || \ | |
625 | (insn->which_extended != IMMNO)) { \ | |
626 | insn->immed[IMMNO] <<= SHAMT; \ | |
627 | } | |
628 | ||
629 | #define DECODE_OPINFO(TAG, BEH) \ | |
630 | case TAG: \ | |
631 | { BEH } \ | |
632 | break; \ | |
633 | ||
634 | /* | |
635 | * Fill in the operands of the instruction | |
636 | * dectree_generated.h.inc has a DECODE_OPINFO entry for each opcode | |
637 | * For example, | |
638 | * DECODE_OPINFO(A2_addi, | |
639 | * DECODE_REG(0,5,0) | |
640 | * DECODE_REG(1,5,16) | |
641 | * DECODE_IMM(0,7,21,9) | |
642 | * DECODE_IMM(0,9,5,0) | |
643 | * DECODE_IMM_SXT(0,16) | |
644 | * with the macros defined above, we'll fill in a switch statement | |
645 | * where each case is an opcode tag. | |
646 | */ | |
647 | static void | |
648 | decode_op(Insn *insn, Opcode tag, uint32_t encoding) | |
649 | { | |
650 | insn->immed[0] = 0; | |
651 | insn->immed[1] = 0; | |
652 | insn->opcode = tag; | |
653 | if (insn->extension_valid) { | |
654 | insn->which_extended = opcode_which_immediate_is_extended(tag); | |
655 | } | |
656 | ||
657 | switch (tag) { | |
658 | #include "dectree_generated.h.inc" | |
659 | default: | |
660 | break; | |
661 | } | |
662 | ||
663 | insn->generate = opcode_genptr[tag]; | |
664 | ||
665 | insn->iclass = iclass_bits(encoding); | |
666 | } | |
667 | ||
668 | #undef DECODE_REG | |
669 | #undef DECODE_IMPL_REG | |
670 | #undef DECODE_IMM | |
671 | #undef DECODE_IMM_SHIFT | |
672 | #undef DECODE_OPINFO | |
673 | #undef DECODE_MATCH_INFO | |
674 | #undef DECODE_LEGACY_MATCH_INFO | |
675 | #undef DECODE_END_TABLE | |
676 | #undef INVALID | |
677 | #undef TERMINAL | |
678 | #undef SUBINSNS | |
679 | #undef EXTSPACE | |
680 | #undef TABLE_LINK | |
681 | #undef DECODE_NEW_TABLE | |
682 | #undef DECODE_SEPARATOR_BITS | |
683 | ||
684 | static unsigned int | |
685 | decode_subinsn_tablewalk(Insn *insn, const DectreeTable *table, | |
686 | uint32_t encoding) | |
687 | { | |
688 | unsigned int i; | |
689 | Opcode opc; | |
690 | if (table->lookup_function) { | |
691 | i = table->lookup_function(table->startbit, table->width, encoding); | |
692 | } else { | |
693 | i = extract32(encoding, table->startbit, table->width); | |
694 | } | |
695 | if (table->table[i].type == DECTREE_TABLE_LINK) { | |
696 | return decode_subinsn_tablewalk(insn, table->table[i].table_link, | |
697 | encoding); | |
698 | } else if (table->table[i].type == DECTREE_TERMINAL) { | |
699 | opc = table->table[i].opcode; | |
700 | if ((encoding & decode_itable[opc].mask) != decode_itable[opc].match) { | |
701 | return 0; | |
702 | } | |
703 | decode_op(insn, opc, encoding); | |
704 | return 1; | |
705 | } else { | |
706 | return 0; | |
707 | } | |
708 | } | |
709 | ||
710 | static unsigned int get_insn_a(uint32_t encoding) | |
711 | { | |
712 | return extract32(encoding, 0, 13); | |
713 | } | |
714 | ||
715 | static unsigned int get_insn_b(uint32_t encoding) | |
716 | { | |
717 | return extract32(encoding, 16, 13); | |
718 | } | |
719 | ||
720 | static unsigned int | |
721 | decode_insns_tablewalk(Insn *insn, const DectreeTable *table, | |
722 | uint32_t encoding) | |
723 | { | |
724 | unsigned int i; | |
725 | unsigned int a, b; | |
726 | Opcode opc; | |
727 | if (table->lookup_function) { | |
728 | i = table->lookup_function(table->startbit, table->width, encoding); | |
729 | } else { | |
730 | i = extract32(encoding, table->startbit, table->width); | |
731 | } | |
732 | if (table->table[i].type == DECTREE_TABLE_LINK) { | |
733 | return decode_insns_tablewalk(insn, table->table[i].table_link, | |
734 | encoding); | |
735 | } else if (table->table[i].type == DECTREE_SUBINSNS) { | |
736 | a = get_insn_a(encoding); | |
737 | b = get_insn_b(encoding); | |
738 | b = decode_subinsn_tablewalk(insn, table->table[i].table_link_b, b); | |
739 | a = decode_subinsn_tablewalk(insn + 1, table->table[i].table_link, a); | |
740 | if ((a == 0) || (b == 0)) { | |
741 | return 0; | |
742 | } | |
743 | return 2; | |
744 | } else if (table->table[i].type == DECTREE_TERMINAL) { | |
745 | opc = table->table[i].opcode; | |
746 | if ((encoding & decode_itable[opc].mask) != decode_itable[opc].match) { | |
747 | if ((encoding & decode_legacy_itable[opc].mask) != | |
748 | decode_legacy_itable[opc].match) { | |
749 | return 0; | |
750 | } | |
751 | } | |
752 | decode_op(insn, opc, encoding); | |
753 | return 1; | |
60d1180b TS |
754 | } else if (table->table[i].type == DECTREE_EXTSPACE) { |
755 | /* | |
756 | * For now, HVX will be the only coproc | |
757 | */ | |
758 | return decode_insns_tablewalk(insn, ext_trees[EXT_IDX_mmvec], encoding); | |
66d29a5e TS |
759 | } else { |
760 | return 0; | |
761 | } | |
762 | } | |
763 | ||
764 | static unsigned int | |
765 | decode_insns(Insn *insn, uint32_t encoding) | |
766 | { | |
767 | const DectreeTable *table; | |
768 | if (parse_bits(encoding) != 0) { | |
769 | /* Start with PP table - 32 bit instructions */ | |
770 | table = &dectree_table_DECODE_ROOT_32; | |
771 | } else { | |
772 | /* start with EE table - duplex instructions */ | |
773 | table = &dectree_table_DECODE_ROOT_EE; | |
774 | } | |
775 | return decode_insns_tablewalk(insn, table, encoding); | |
776 | } | |
777 | ||
778 | static void decode_add_endloop_insn(Insn *insn, int loopnum) | |
779 | { | |
780 | if (loopnum == 10) { | |
781 | insn->opcode = J2_endloop01; | |
782 | insn->generate = opcode_genptr[J2_endloop01]; | |
783 | } else if (loopnum == 1) { | |
784 | insn->opcode = J2_endloop1; | |
785 | insn->generate = opcode_genptr[J2_endloop1]; | |
786 | } else if (loopnum == 0) { | |
787 | insn->opcode = J2_endloop0; | |
788 | insn->generate = opcode_genptr[J2_endloop0]; | |
789 | } else { | |
790 | g_assert_not_reached(); | |
791 | } | |
792 | } | |
793 | ||
92cfa25f | 794 | static bool decode_parsebits_is_loopend(uint32_t encoding32) |
66d29a5e TS |
795 | { |
796 | uint32_t bits = parse_bits(encoding32); | |
797 | return bits == 0x2; | |
798 | } | |
799 | ||
800 | static void | |
801 | decode_set_slot_number(Packet *pkt) | |
802 | { | |
803 | int slot; | |
804 | int i; | |
92cfa25f TS |
805 | bool hit_mem_insn = false; |
806 | bool hit_duplex = false; | |
807 | bool slot0_found = false; | |
808 | bool slot1_found = false; | |
809 | int slot1_iidx = 0; | |
66d29a5e TS |
810 | |
811 | /* | |
812 | * The slots are encoded in reverse order | |
813 | * For each instruction, count down until you find a suitable slot | |
814 | */ | |
815 | for (i = 0, slot = 3; i < pkt->num_insns; i++) { | |
816 | SlotMask valid_slots = get_valid_slots(pkt, i); | |
817 | ||
818 | while (!(valid_slots & (1 << slot))) { | |
819 | slot--; | |
820 | } | |
821 | pkt->insn[i].slot = slot; | |
822 | if (slot) { | |
823 | /* I've assigned the slot, now decrement it for the next insn */ | |
824 | slot--; | |
825 | } | |
826 | } | |
827 | ||
828 | /* Fix the exceptions - mem insns to slot 0,1 */ | |
829 | for (i = pkt->num_insns - 1; i >= 0; i--) { | |
830 | /* First memory instruction always goes to slot 0 */ | |
831 | if ((GET_ATTRIB(pkt->insn[i].opcode, A_MEMLIKE) || | |
832 | GET_ATTRIB(pkt->insn[i].opcode, A_MEMLIKE_PACKET_RULES)) && | |
833 | !hit_mem_insn) { | |
92cfa25f | 834 | hit_mem_insn = true; |
66d29a5e TS |
835 | pkt->insn[i].slot = 0; |
836 | continue; | |
837 | } | |
838 | ||
839 | /* Next memory instruction always goes to slot 1 */ | |
840 | if ((GET_ATTRIB(pkt->insn[i].opcode, A_MEMLIKE) || | |
841 | GET_ATTRIB(pkt->insn[i].opcode, A_MEMLIKE_PACKET_RULES)) && | |
842 | hit_mem_insn) { | |
843 | pkt->insn[i].slot = 1; | |
844 | } | |
845 | } | |
846 | ||
847 | /* Fix the exceptions - duplex always slot 0,1 */ | |
848 | for (i = pkt->num_insns - 1; i >= 0; i--) { | |
849 | /* First subinsn always goes to slot 0 */ | |
850 | if (GET_ATTRIB(pkt->insn[i].opcode, A_SUBINSN) && !hit_duplex) { | |
92cfa25f | 851 | hit_duplex = true; |
66d29a5e TS |
852 | pkt->insn[i].slot = 0; |
853 | continue; | |
854 | } | |
855 | ||
856 | /* Next subinsn always goes to slot 1 */ | |
857 | if (GET_ATTRIB(pkt->insn[i].opcode, A_SUBINSN) && hit_duplex) { | |
858 | pkt->insn[i].slot = 1; | |
859 | } | |
860 | } | |
861 | ||
862 | /* Fix the exceptions - slot 1 is never empty, always aligns to slot 0 */ | |
66d29a5e TS |
863 | for (i = pkt->num_insns - 1; i >= 0; i--) { |
864 | /* Is slot0 used? */ | |
865 | if (pkt->insn[i].slot == 0) { | |
92cfa25f | 866 | bool is_endloop = (pkt->insn[i].opcode == J2_endloop01); |
66d29a5e TS |
867 | is_endloop |= (pkt->insn[i].opcode == J2_endloop0); |
868 | is_endloop |= (pkt->insn[i].opcode == J2_endloop1); | |
869 | ||
870 | /* | |
871 | * Make sure it's not endloop since, we're overloading | |
872 | * slot0 for endloop | |
873 | */ | |
874 | if (!is_endloop) { | |
92cfa25f | 875 | slot0_found = true; |
66d29a5e TS |
876 | } |
877 | } | |
878 | /* Is slot1 used? */ | |
879 | if (pkt->insn[i].slot == 1) { | |
92cfa25f | 880 | slot1_found = true; |
66d29a5e TS |
881 | slot1_iidx = i; |
882 | } | |
883 | } | |
884 | /* Is slot0 empty and slot1 used? */ | |
92cfa25f | 885 | if ((!slot0_found) && slot1_found) { |
66d29a5e TS |
886 | /* Then push it to slot0 */ |
887 | pkt->insn[slot1_iidx].slot = 0; | |
888 | } | |
889 | } | |
890 | ||
891 | /* | |
892 | * decode_packet | |
893 | * Decodes packet with given words | |
894 | * Returns 0 on insufficient words, | |
895 | * or number of words used on success | |
896 | */ | |
897 | ||
898 | int decode_packet(int max_words, const uint32_t *words, Packet *pkt, | |
899 | bool disas_only) | |
900 | { | |
901 | int num_insns = 0; | |
902 | int words_read = 0; | |
92cfa25f | 903 | bool end_of_packet = false; |
66d29a5e | 904 | int new_insns = 0; |
60d1180b | 905 | int i; |
66d29a5e TS |
906 | uint32_t encoding32; |
907 | ||
908 | /* Initialize */ | |
909 | memset(pkt, 0, sizeof(*pkt)); | |
910 | /* Try to build packet */ | |
911 | while (!end_of_packet && (words_read < max_words)) { | |
912 | encoding32 = words[words_read]; | |
913 | end_of_packet = is_packet_end(encoding32); | |
914 | new_insns = decode_insns(&pkt->insn[num_insns], encoding32); | |
915 | g_assert(new_insns > 0); | |
916 | /* | |
917 | * If we saw an extender, mark next word extended so immediate | |
918 | * decode works | |
919 | */ | |
920 | if (pkt->insn[num_insns].opcode == A4_ext) { | |
92cfa25f | 921 | pkt->insn[num_insns + 1].extension_valid = true; |
66d29a5e TS |
922 | } |
923 | num_insns += new_insns; | |
924 | words_read++; | |
925 | } | |
926 | ||
927 | pkt->num_insns = num_insns; | |
928 | if (!end_of_packet) { | |
929 | /* Ran out of words! */ | |
930 | return 0; | |
931 | } | |
932 | pkt->encod_pkt_size_in_bytes = words_read * 4; | |
60d1180b TS |
933 | pkt->pkt_has_hvx = false; |
934 | for (i = 0; i < num_insns; i++) { | |
935 | pkt->pkt_has_hvx |= | |
936 | GET_ATTRIB(pkt->insn[i].opcode, A_CVI); | |
937 | } | |
66d29a5e TS |
938 | |
939 | /* | |
940 | * Check for :endloop in the parse bits | |
941 | * Section 10.6 of the Programmer's Reference describes the encoding | |
942 | * The end of hardware loop 0 can be encoded with 2 words | |
943 | * The end of hardware loop 1 needs 3 words | |
944 | */ | |
945 | if ((words_read == 2) && (decode_parsebits_is_loopend(words[0]))) { | |
946 | decode_add_endloop_insn(&pkt->insn[pkt->num_insns++], 0); | |
947 | } | |
948 | if (words_read >= 3) { | |
92cfa25f | 949 | bool has_loop0, has_loop1; |
66d29a5e TS |
950 | has_loop0 = decode_parsebits_is_loopend(words[0]); |
951 | has_loop1 = decode_parsebits_is_loopend(words[1]); | |
952 | if (has_loop0 && has_loop1) { | |
953 | decode_add_endloop_insn(&pkt->insn[pkt->num_insns++], 10); | |
954 | } else if (has_loop1) { | |
955 | decode_add_endloop_insn(&pkt->insn[pkt->num_insns++], 1); | |
956 | } else if (has_loop0) { | |
957 | decode_add_endloop_insn(&pkt->insn[pkt->num_insns++], 0); | |
958 | } | |
959 | } | |
960 | ||
961 | decode_apply_extenders(pkt); | |
962 | if (!disas_only) { | |
963 | decode_remove_extenders(pkt); | |
964 | } | |
965 | decode_set_slot_number(pkt); | |
966 | decode_fill_newvalue_regno(pkt); | |
967 | ||
60d1180b TS |
968 | if (pkt->pkt_has_hvx) { |
969 | mmvec_ext_decode_checks(pkt, disas_only); | |
970 | } | |
971 | ||
66d29a5e TS |
972 | if (!disas_only) { |
973 | decode_shuffle_for_execution(pkt); | |
974 | decode_split_cmpjump(pkt); | |
975 | decode_set_insn_attr_fields(pkt); | |
976 | } | |
977 | ||
978 | return words_read; | |
979 | } | |
980 | ||
981 | /* Used for "-d in_asm" logging */ | |
982 | int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, | |
983 | GString *buf) | |
984 | { | |
985 | Packet pkt; | |
986 | ||
987 | if (decode_packet(nwords, words, &pkt, true) > 0) { | |
988 | snprint_a_pkt_disas(buf, &pkt, words, pc); | |
989 | return pkt.encod_pkt_size_in_bytes; | |
990 | } else { | |
991 | g_string_assign(buf, "<invalid>"); | |
992 | return 0; | |
993 | } | |
994 | } |