OLD | NEW |
| (Empty) |
1 // Copyright 2013 the V8 project authors. All rights reserved. | |
2 // Redistribution and use in source and binary forms, with or without | |
3 // modification, are permitted provided that the following conditions are | |
4 // met: | |
5 // | |
6 // * Redistributions of source code must retain the above copyright | |
7 // notice, this list of conditions and the following disclaimer. | |
8 // * Redistributions in binary form must reproduce the above | |
9 // copyright notice, this list of conditions and the following | |
10 // disclaimer in the documentation and/or other materials provided | |
11 // with the distribution. | |
12 // * Neither the name of Google Inc. nor the names of its | |
13 // contributors may be used to endorse or promote products derived | |
14 // from this software without specific prior written permission. | |
15 // | |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 #include <assert.h> | |
29 #include <stdio.h> | |
30 #include <stdarg.h> | |
31 #include <string.h> | |
32 | |
33 #include "v8.h" | |
34 | |
35 #if V8_TARGET_ARCH_A64 | |
36 | |
37 #include "disasm.h" | |
38 #include "a64/decoder-a64-inl.h" | |
39 #include "a64/disasm-a64.h" | |
40 #include "macro-assembler.h" | |
41 #include "platform.h" | |
42 | |
43 namespace v8 { | |
44 namespace internal { | |
45 | |
46 | |
47 Disassembler::Disassembler() { | |
48 buffer_size_ = 256; | |
49 buffer_ = reinterpret_cast<char*>(malloc(buffer_size_)); | |
50 buffer_pos_ = 0; | |
51 own_buffer_ = true; | |
52 } | |
53 | |
54 | |
55 Disassembler::Disassembler(char* text_buffer, int buffer_size) { | |
56 buffer_size_ = buffer_size; | |
57 buffer_ = text_buffer; | |
58 buffer_pos_ = 0; | |
59 own_buffer_ = false; | |
60 } | |
61 | |
62 | |
63 Disassembler::~Disassembler() { | |
64 if (own_buffer_) { | |
65 free(buffer_); | |
66 } | |
67 } | |
68 | |
69 | |
70 char* Disassembler::GetOutput() { | |
71 return buffer_; | |
72 } | |
73 | |
74 | |
75 void Disassembler::VisitAddSubImmediate(Instruction* instr) { | |
76 bool rd_is_zr = RdIsZROrSP(instr); | |
77 bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && | |
78 (instr->ImmAddSub() == 0) ? true : false; | |
79 const char *mnemonic = ""; | |
80 const char *form = "'Rds, 'Rns, 'IAddSub"; | |
81 const char *form_cmp = "'Rns, 'IAddSub"; | |
82 const char *form_mov = "'Rds, 'Rns"; | |
83 | |
84 switch (instr->Mask(AddSubImmediateMask)) { | |
85 case ADD_w_imm: | |
86 case ADD_x_imm: { | |
87 mnemonic = "add"; | |
88 if (stack_op) { | |
89 mnemonic = "mov"; | |
90 form = form_mov; | |
91 } | |
92 break; | |
93 } | |
94 case ADDS_w_imm: | |
95 case ADDS_x_imm: { | |
96 mnemonic = "adds"; | |
97 if (rd_is_zr) { | |
98 mnemonic = "cmn"; | |
99 form = form_cmp; | |
100 } | |
101 break; | |
102 } | |
103 case SUB_w_imm: | |
104 case SUB_x_imm: mnemonic = "sub"; break; | |
105 case SUBS_w_imm: | |
106 case SUBS_x_imm: { | |
107 mnemonic = "subs"; | |
108 if (rd_is_zr) { | |
109 mnemonic = "cmp"; | |
110 form = form_cmp; | |
111 } | |
112 break; | |
113 } | |
114 default: UNREACHABLE(); | |
115 } | |
116 Format(instr, mnemonic, form); | |
117 } | |
118 | |
119 | |
120 void Disassembler::VisitAddSubShifted(Instruction* instr) { | |
121 bool rd_is_zr = RdIsZROrSP(instr); | |
122 bool rn_is_zr = RnIsZROrSP(instr); | |
123 const char *mnemonic = ""; | |
124 const char *form = "'Rd, 'Rn, 'Rm'HDP"; | |
125 const char *form_cmp = "'Rn, 'Rm'HDP"; | |
126 const char *form_neg = "'Rd, 'Rm'HDP"; | |
127 | |
128 switch (instr->Mask(AddSubShiftedMask)) { | |
129 case ADD_w_shift: | |
130 case ADD_x_shift: mnemonic = "add"; break; | |
131 case ADDS_w_shift: | |
132 case ADDS_x_shift: { | |
133 mnemonic = "adds"; | |
134 if (rd_is_zr) { | |
135 mnemonic = "cmn"; | |
136 form = form_cmp; | |
137 } | |
138 break; | |
139 } | |
140 case SUB_w_shift: | |
141 case SUB_x_shift: { | |
142 mnemonic = "sub"; | |
143 if (rn_is_zr) { | |
144 mnemonic = "neg"; | |
145 form = form_neg; | |
146 } | |
147 break; | |
148 } | |
149 case SUBS_w_shift: | |
150 case SUBS_x_shift: { | |
151 mnemonic = "subs"; | |
152 if (rd_is_zr) { | |
153 mnemonic = "cmp"; | |
154 form = form_cmp; | |
155 } else if (rn_is_zr) { | |
156 mnemonic = "negs"; | |
157 form = form_neg; | |
158 } | |
159 break; | |
160 } | |
161 default: UNREACHABLE(); | |
162 } | |
163 Format(instr, mnemonic, form); | |
164 } | |
165 | |
166 | |
167 void Disassembler::VisitAddSubExtended(Instruction* instr) { | |
168 bool rd_is_zr = RdIsZROrSP(instr); | |
169 const char *mnemonic = ""; | |
170 Extend mode = static_cast<Extend>(instr->ExtendMode()); | |
171 const char *form = ((mode == UXTX) || (mode == SXTX)) ? | |
172 "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; | |
173 const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? | |
174 "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; | |
175 | |
176 switch (instr->Mask(AddSubExtendedMask)) { | |
177 case ADD_w_ext: | |
178 case ADD_x_ext: mnemonic = "add"; break; | |
179 case ADDS_w_ext: | |
180 case ADDS_x_ext: { | |
181 mnemonic = "adds"; | |
182 if (rd_is_zr) { | |
183 mnemonic = "cmn"; | |
184 form = form_cmp; | |
185 } | |
186 break; | |
187 } | |
188 case SUB_w_ext: | |
189 case SUB_x_ext: mnemonic = "sub"; break; | |
190 case SUBS_w_ext: | |
191 case SUBS_x_ext: { | |
192 mnemonic = "subs"; | |
193 if (rd_is_zr) { | |
194 mnemonic = "cmp"; | |
195 form = form_cmp; | |
196 } | |
197 break; | |
198 } | |
199 default: UNREACHABLE(); | |
200 } | |
201 Format(instr, mnemonic, form); | |
202 } | |
203 | |
204 | |
205 void Disassembler::VisitAddSubWithCarry(Instruction* instr) { | |
206 bool rn_is_zr = RnIsZROrSP(instr); | |
207 const char *mnemonic = ""; | |
208 const char *form = "'Rd, 'Rn, 'Rm"; | |
209 const char *form_neg = "'Rd, 'Rm"; | |
210 | |
211 switch (instr->Mask(AddSubWithCarryMask)) { | |
212 case ADC_w: | |
213 case ADC_x: mnemonic = "adc"; break; | |
214 case ADCS_w: | |
215 case ADCS_x: mnemonic = "adcs"; break; | |
216 case SBC_w: | |
217 case SBC_x: { | |
218 mnemonic = "sbc"; | |
219 if (rn_is_zr) { | |
220 mnemonic = "ngc"; | |
221 form = form_neg; | |
222 } | |
223 break; | |
224 } | |
225 case SBCS_w: | |
226 case SBCS_x: { | |
227 mnemonic = "sbcs"; | |
228 if (rn_is_zr) { | |
229 mnemonic = "ngcs"; | |
230 form = form_neg; | |
231 } | |
232 break; | |
233 } | |
234 default: UNREACHABLE(); | |
235 } | |
236 Format(instr, mnemonic, form); | |
237 } | |
238 | |
239 | |
240 void Disassembler::VisitLogicalImmediate(Instruction* instr) { | |
241 bool rd_is_zr = RdIsZROrSP(instr); | |
242 bool rn_is_zr = RnIsZROrSP(instr); | |
243 const char *mnemonic = ""; | |
244 const char *form = "'Rds, 'Rn, 'ITri"; | |
245 | |
246 if (instr->ImmLogical() == 0) { | |
247 // The immediate encoded in the instruction is not in the expected format. | |
248 Format(instr, "unallocated", "(LogicalImmediate)"); | |
249 return; | |
250 } | |
251 | |
252 switch (instr->Mask(LogicalImmediateMask)) { | |
253 case AND_w_imm: | |
254 case AND_x_imm: mnemonic = "and"; break; | |
255 case ORR_w_imm: | |
256 case ORR_x_imm: { | |
257 mnemonic = "orr"; | |
258 unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSizeInBits | |
259 : kWRegSizeInBits; | |
260 if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { | |
261 mnemonic = "mov"; | |
262 form = "'Rds, 'ITri"; | |
263 } | |
264 break; | |
265 } | |
266 case EOR_w_imm: | |
267 case EOR_x_imm: mnemonic = "eor"; break; | |
268 case ANDS_w_imm: | |
269 case ANDS_x_imm: { | |
270 mnemonic = "ands"; | |
271 if (rd_is_zr) { | |
272 mnemonic = "tst"; | |
273 form = "'Rn, 'ITri"; | |
274 } | |
275 break; | |
276 } | |
277 default: UNREACHABLE(); | |
278 } | |
279 Format(instr, mnemonic, form); | |
280 } | |
281 | |
282 | |
283 bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { | |
284 ASSERT((reg_size == kXRegSizeInBits) || | |
285 ((reg_size == kWRegSizeInBits) && (value <= 0xffffffff))); | |
286 | |
287 // Test for movz: 16-bits set at positions 0, 16, 32 or 48. | |
288 if (((value & 0xffffffffffff0000UL) == 0UL) || | |
289 ((value & 0xffffffff0000ffffUL) == 0UL) || | |
290 ((value & 0xffff0000ffffffffUL) == 0UL) || | |
291 ((value & 0x0000ffffffffffffUL) == 0UL)) { | |
292 return true; | |
293 } | |
294 | |
295 // Test for movn: NOT(16-bits set at positions 0, 16, 32 or 48). | |
296 if ((reg_size == kXRegSizeInBits) && | |
297 (((value & 0xffffffffffff0000UL) == 0xffffffffffff0000UL) || | |
298 ((value & 0xffffffff0000ffffUL) == 0xffffffff0000ffffUL) || | |
299 ((value & 0xffff0000ffffffffUL) == 0xffff0000ffffffffUL) || | |
300 ((value & 0x0000ffffffffffffUL) == 0x0000ffffffffffffUL))) { | |
301 return true; | |
302 } | |
303 if ((reg_size == kWRegSizeInBits) && | |
304 (((value & 0xffff0000) == 0xffff0000) || | |
305 ((value & 0x0000ffff) == 0x0000ffff))) { | |
306 return true; | |
307 } | |
308 return false; | |
309 } | |
310 | |
311 | |
312 void Disassembler::VisitLogicalShifted(Instruction* instr) { | |
313 bool rd_is_zr = RdIsZROrSP(instr); | |
314 bool rn_is_zr = RnIsZROrSP(instr); | |
315 const char *mnemonic = ""; | |
316 const char *form = "'Rd, 'Rn, 'Rm'HLo"; | |
317 | |
318 switch (instr->Mask(LogicalShiftedMask)) { | |
319 case AND_w: | |
320 case AND_x: mnemonic = "and"; break; | |
321 case BIC_w: | |
322 case BIC_x: mnemonic = "bic"; break; | |
323 case EOR_w: | |
324 case EOR_x: mnemonic = "eor"; break; | |
325 case EON_w: | |
326 case EON_x: mnemonic = "eon"; break; | |
327 case BICS_w: | |
328 case BICS_x: mnemonic = "bics"; break; | |
329 case ANDS_w: | |
330 case ANDS_x: { | |
331 mnemonic = "ands"; | |
332 if (rd_is_zr) { | |
333 mnemonic = "tst"; | |
334 form = "'Rn, 'Rm'HLo"; | |
335 } | |
336 break; | |
337 } | |
338 case ORR_w: | |
339 case ORR_x: { | |
340 mnemonic = "orr"; | |
341 if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { | |
342 mnemonic = "mov"; | |
343 form = "'Rd, 'Rm"; | |
344 } | |
345 break; | |
346 } | |
347 case ORN_w: | |
348 case ORN_x: { | |
349 mnemonic = "orn"; | |
350 if (rn_is_zr) { | |
351 mnemonic = "mvn"; | |
352 form = "'Rd, 'Rm'HLo"; | |
353 } | |
354 break; | |
355 } | |
356 default: UNREACHABLE(); | |
357 } | |
358 | |
359 Format(instr, mnemonic, form); | |
360 } | |
361 | |
362 | |
363 void Disassembler::VisitConditionalCompareRegister(Instruction* instr) { | |
364 const char *mnemonic = ""; | |
365 const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; | |
366 | |
367 switch (instr->Mask(ConditionalCompareRegisterMask)) { | |
368 case CCMN_w: | |
369 case CCMN_x: mnemonic = "ccmn"; break; | |
370 case CCMP_w: | |
371 case CCMP_x: mnemonic = "ccmp"; break; | |
372 default: UNREACHABLE(); | |
373 } | |
374 Format(instr, mnemonic, form); | |
375 } | |
376 | |
377 | |
378 void Disassembler::VisitConditionalCompareImmediate(Instruction* instr) { | |
379 const char *mnemonic = ""; | |
380 const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; | |
381 | |
382 switch (instr->Mask(ConditionalCompareImmediateMask)) { | |
383 case CCMN_w_imm: | |
384 case CCMN_x_imm: mnemonic = "ccmn"; break; | |
385 case CCMP_w_imm: | |
386 case CCMP_x_imm: mnemonic = "ccmp"; break; | |
387 default: UNREACHABLE(); | |
388 } | |
389 Format(instr, mnemonic, form); | |
390 } | |
391 | |
392 | |
393 void Disassembler::VisitConditionalSelect(Instruction* instr) { | |
394 bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); | |
395 bool rn_is_rm = (instr->Rn() == instr->Rm()); | |
396 const char *mnemonic = ""; | |
397 const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; | |
398 const char *form_test = "'Rd, 'CInv"; | |
399 const char *form_update = "'Rd, 'Rn, 'CInv"; | |
400 | |
401 Condition cond = static_cast<Condition>(instr->Condition()); | |
402 bool invertible_cond = (cond != al) && (cond != nv); | |
403 | |
404 switch (instr->Mask(ConditionalSelectMask)) { | |
405 case CSEL_w: | |
406 case CSEL_x: mnemonic = "csel"; break; | |
407 case CSINC_w: | |
408 case CSINC_x: { | |
409 mnemonic = "csinc"; | |
410 if (rnm_is_zr && invertible_cond) { | |
411 mnemonic = "cset"; | |
412 form = form_test; | |
413 } else if (rn_is_rm && invertible_cond) { | |
414 mnemonic = "cinc"; | |
415 form = form_update; | |
416 } | |
417 break; | |
418 } | |
419 case CSINV_w: | |
420 case CSINV_x: { | |
421 mnemonic = "csinv"; | |
422 if (rnm_is_zr && invertible_cond) { | |
423 mnemonic = "csetm"; | |
424 form = form_test; | |
425 } else if (rn_is_rm && invertible_cond) { | |
426 mnemonic = "cinv"; | |
427 form = form_update; | |
428 } | |
429 break; | |
430 } | |
431 case CSNEG_w: | |
432 case CSNEG_x: { | |
433 mnemonic = "csneg"; | |
434 if (rn_is_rm && invertible_cond) { | |
435 mnemonic = "cneg"; | |
436 form = form_update; | |
437 } | |
438 break; | |
439 } | |
440 default: UNREACHABLE(); | |
441 } | |
442 Format(instr, mnemonic, form); | |
443 } | |
444 | |
445 | |
446 void Disassembler::VisitBitfield(Instruction* instr) { | |
447 unsigned s = instr->ImmS(); | |
448 unsigned r = instr->ImmR(); | |
449 unsigned rd_size_minus_1 = | |
450 ((instr->SixtyFourBits() == 1) ? kXRegSizeInBits : kWRegSizeInBits) - 1; | |
451 const char *mnemonic = ""; | |
452 const char *form = ""; | |
453 const char *form_shift_right = "'Rd, 'Rn, 'IBr"; | |
454 const char *form_extend = "'Rd, 'Wn"; | |
455 const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; | |
456 const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; | |
457 const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; | |
458 | |
459 switch (instr->Mask(BitfieldMask)) { | |
460 case SBFM_w: | |
461 case SBFM_x: { | |
462 mnemonic = "sbfx"; | |
463 form = form_bfx; | |
464 if (r == 0) { | |
465 form = form_extend; | |
466 if (s == 7) { | |
467 mnemonic = "sxtb"; | |
468 } else if (s == 15) { | |
469 mnemonic = "sxth"; | |
470 } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { | |
471 mnemonic = "sxtw"; | |
472 } else { | |
473 form = form_bfx; | |
474 } | |
475 } else if (s == rd_size_minus_1) { | |
476 mnemonic = "asr"; | |
477 form = form_shift_right; | |
478 } else if (s < r) { | |
479 mnemonic = "sbfiz"; | |
480 form = form_bfiz; | |
481 } | |
482 break; | |
483 } | |
484 case UBFM_w: | |
485 case UBFM_x: { | |
486 mnemonic = "ubfx"; | |
487 form = form_bfx; | |
488 if (r == 0) { | |
489 form = form_extend; | |
490 if (s == 7) { | |
491 mnemonic = "uxtb"; | |
492 } else if (s == 15) { | |
493 mnemonic = "uxth"; | |
494 } else { | |
495 form = form_bfx; | |
496 } | |
497 } | |
498 if (s == rd_size_minus_1) { | |
499 mnemonic = "lsr"; | |
500 form = form_shift_right; | |
501 } else if (r == s + 1) { | |
502 mnemonic = "lsl"; | |
503 form = form_lsl; | |
504 } else if (s < r) { | |
505 mnemonic = "ubfiz"; | |
506 form = form_bfiz; | |
507 } | |
508 break; | |
509 } | |
510 case BFM_w: | |
511 case BFM_x: { | |
512 mnemonic = "bfxil"; | |
513 form = form_bfx; | |
514 if (s < r) { | |
515 mnemonic = "bfi"; | |
516 form = form_bfiz; | |
517 } | |
518 } | |
519 } | |
520 Format(instr, mnemonic, form); | |
521 } | |
522 | |
523 | |
524 void Disassembler::VisitExtract(Instruction* instr) { | |
525 const char *mnemonic = ""; | |
526 const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; | |
527 | |
528 switch (instr->Mask(ExtractMask)) { | |
529 case EXTR_w: | |
530 case EXTR_x: { | |
531 if (instr->Rn() == instr->Rm()) { | |
532 mnemonic = "ror"; | |
533 form = "'Rd, 'Rn, 'IExtract"; | |
534 } else { | |
535 mnemonic = "extr"; | |
536 } | |
537 break; | |
538 } | |
539 default: UNREACHABLE(); | |
540 } | |
541 Format(instr, mnemonic, form); | |
542 } | |
543 | |
544 | |
545 void Disassembler::VisitPCRelAddressing(Instruction* instr) { | |
546 switch (instr->Mask(PCRelAddressingMask)) { | |
547 case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; | |
548 // ADRP is not implemented. | |
549 default: Format(instr, "unimplemented", "(PCRelAddressing)"); | |
550 } | |
551 } | |
552 | |
553 | |
554 void Disassembler::VisitConditionalBranch(Instruction* instr) { | |
555 switch (instr->Mask(ConditionalBranchMask)) { | |
556 case B_cond: Format(instr, "b.'CBrn", "'BImmCond"); break; | |
557 default: UNREACHABLE(); | |
558 } | |
559 } | |
560 | |
561 | |
562 void Disassembler::VisitUnconditionalBranchToRegister(Instruction* instr) { | |
563 const char *mnemonic = "unimplemented"; | |
564 const char *form = "'Xn"; | |
565 | |
566 switch (instr->Mask(UnconditionalBranchToRegisterMask)) { | |
567 case BR: mnemonic = "br"; break; | |
568 case BLR: mnemonic = "blr"; break; | |
569 case RET: { | |
570 mnemonic = "ret"; | |
571 if (instr->Rn() == kLinkRegCode) { | |
572 form = NULL; | |
573 } | |
574 break; | |
575 } | |
576 default: form = "(UnconditionalBranchToRegister)"; | |
577 } | |
578 Format(instr, mnemonic, form); | |
579 } | |
580 | |
581 | |
582 void Disassembler::VisitUnconditionalBranch(Instruction* instr) { | |
583 const char *mnemonic = ""; | |
584 const char *form = "'BImmUncn"; | |
585 | |
586 switch (instr->Mask(UnconditionalBranchMask)) { | |
587 case B: mnemonic = "b"; break; | |
588 case BL: mnemonic = "bl"; break; | |
589 default: UNREACHABLE(); | |
590 } | |
591 Format(instr, mnemonic, form); | |
592 } | |
593 | |
594 | |
595 void Disassembler::VisitDataProcessing1Source(Instruction* instr) { | |
596 const char *mnemonic = ""; | |
597 const char *form = "'Rd, 'Rn"; | |
598 | |
599 switch (instr->Mask(DataProcessing1SourceMask)) { | |
600 #define FORMAT(A, B) \ | |
601 case A##_w: \ | |
602 case A##_x: mnemonic = B; break; | |
603 FORMAT(RBIT, "rbit"); | |
604 FORMAT(REV16, "rev16"); | |
605 FORMAT(REV, "rev"); | |
606 FORMAT(CLZ, "clz"); | |
607 FORMAT(CLS, "cls"); | |
608 #undef FORMAT | |
609 case REV32_x: mnemonic = "rev32"; break; | |
610 default: UNREACHABLE(); | |
611 } | |
612 Format(instr, mnemonic, form); | |
613 } | |
614 | |
615 | |
616 void Disassembler::VisitDataProcessing2Source(Instruction* instr) { | |
617 const char *mnemonic = "unimplemented"; | |
618 const char *form = "'Rd, 'Rn, 'Rm"; | |
619 | |
620 switch (instr->Mask(DataProcessing2SourceMask)) { | |
621 #define FORMAT(A, B) \ | |
622 case A##_w: \ | |
623 case A##_x: mnemonic = B; break; | |
624 FORMAT(UDIV, "udiv"); | |
625 FORMAT(SDIV, "sdiv"); | |
626 FORMAT(LSLV, "lsl"); | |
627 FORMAT(LSRV, "lsr"); | |
628 FORMAT(ASRV, "asr"); | |
629 FORMAT(RORV, "ror"); | |
630 #undef FORMAT | |
631 default: form = "(DataProcessing2Source)"; | |
632 } | |
633 Format(instr, mnemonic, form); | |
634 } | |
635 | |
636 | |
637 void Disassembler::VisitDataProcessing3Source(Instruction* instr) { | |
638 bool ra_is_zr = RaIsZROrSP(instr); | |
639 const char *mnemonic = ""; | |
640 const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; | |
641 const char *form_rrr = "'Rd, 'Rn, 'Rm"; | |
642 const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; | |
643 const char *form_xww = "'Xd, 'Wn, 'Wm"; | |
644 const char *form_xxx = "'Xd, 'Xn, 'Xm"; | |
645 | |
646 switch (instr->Mask(DataProcessing3SourceMask)) { | |
647 case MADD_w: | |
648 case MADD_x: { | |
649 mnemonic = "madd"; | |
650 form = form_rrrr; | |
651 if (ra_is_zr) { | |
652 mnemonic = "mul"; | |
653 form = form_rrr; | |
654 } | |
655 break; | |
656 } | |
657 case MSUB_w: | |
658 case MSUB_x: { | |
659 mnemonic = "msub"; | |
660 form = form_rrrr; | |
661 if (ra_is_zr) { | |
662 mnemonic = "mneg"; | |
663 form = form_rrr; | |
664 } | |
665 break; | |
666 } | |
667 case SMADDL_x: { | |
668 mnemonic = "smaddl"; | |
669 if (ra_is_zr) { | |
670 mnemonic = "smull"; | |
671 form = form_xww; | |
672 } | |
673 break; | |
674 } | |
675 case SMSUBL_x: { | |
676 mnemonic = "smsubl"; | |
677 if (ra_is_zr) { | |
678 mnemonic = "smnegl"; | |
679 form = form_xww; | |
680 } | |
681 break; | |
682 } | |
683 case UMADDL_x: { | |
684 mnemonic = "umaddl"; | |
685 if (ra_is_zr) { | |
686 mnemonic = "umull"; | |
687 form = form_xww; | |
688 } | |
689 break; | |
690 } | |
691 case UMSUBL_x: { | |
692 mnemonic = "umsubl"; | |
693 if (ra_is_zr) { | |
694 mnemonic = "umnegl"; | |
695 form = form_xww; | |
696 } | |
697 break; | |
698 } | |
699 case SMULH_x: { | |
700 mnemonic = "smulh"; | |
701 form = form_xxx; | |
702 break; | |
703 } | |
704 case UMULH_x: { | |
705 mnemonic = "umulh"; | |
706 form = form_xxx; | |
707 break; | |
708 } | |
709 default: UNREACHABLE(); | |
710 } | |
711 Format(instr, mnemonic, form); | |
712 } | |
713 | |
714 | |
715 void Disassembler::VisitCompareBranch(Instruction* instr) { | |
716 const char *mnemonic = ""; | |
717 const char *form = "'Rt, 'BImmCmpa"; | |
718 | |
719 switch (instr->Mask(CompareBranchMask)) { | |
720 case CBZ_w: | |
721 case CBZ_x: mnemonic = "cbz"; break; | |
722 case CBNZ_w: | |
723 case CBNZ_x: mnemonic = "cbnz"; break; | |
724 default: UNREACHABLE(); | |
725 } | |
726 Format(instr, mnemonic, form); | |
727 } | |
728 | |
729 | |
730 void Disassembler::VisitTestBranch(Instruction* instr) { | |
731 const char *mnemonic = ""; | |
732 // If the top bit of the immediate is clear, the tested register is | |
733 // disassembled as Wt, otherwise Xt. As the top bit of the immediate is | |
734 // encoded in bit 31 of the instruction, we can reuse the Rt form, which | |
735 // uses bit 31 (normally "sf") to choose the register size. | |
736 const char *form = "'Rt, 'IS, 'BImmTest"; | |
737 | |
738 switch (instr->Mask(TestBranchMask)) { | |
739 case TBZ: mnemonic = "tbz"; break; | |
740 case TBNZ: mnemonic = "tbnz"; break; | |
741 default: UNREACHABLE(); | |
742 } | |
743 Format(instr, mnemonic, form); | |
744 } | |
745 | |
746 | |
747 void Disassembler::VisitMoveWideImmediate(Instruction* instr) { | |
748 const char *mnemonic = ""; | |
749 const char *form = "'Rd, 'IMoveImm"; | |
750 | |
751 // Print the shift separately for movk, to make it clear which half word will | |
752 // be overwritten. Movn and movz print the computed immediate, which includes | |
753 // shift calculation. | |
754 switch (instr->Mask(MoveWideImmediateMask)) { | |
755 case MOVN_w: | |
756 case MOVN_x: mnemonic = "movn"; break; | |
757 case MOVZ_w: | |
758 case MOVZ_x: mnemonic = "movz"; break; | |
759 case MOVK_w: | |
760 case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; | |
761 default: UNREACHABLE(); | |
762 } | |
763 Format(instr, mnemonic, form); | |
764 } | |
765 | |
766 | |
767 #define LOAD_STORE_LIST(V) \ | |
768 V(STRB_w, "strb", "'Wt") \ | |
769 V(STRH_w, "strh", "'Wt") \ | |
770 V(STR_w, "str", "'Wt") \ | |
771 V(STR_x, "str", "'Xt") \ | |
772 V(LDRB_w, "ldrb", "'Wt") \ | |
773 V(LDRH_w, "ldrh", "'Wt") \ | |
774 V(LDR_w, "ldr", "'Wt") \ | |
775 V(LDR_x, "ldr", "'Xt") \ | |
776 V(LDRSB_x, "ldrsb", "'Xt") \ | |
777 V(LDRSH_x, "ldrsh", "'Xt") \ | |
778 V(LDRSW_x, "ldrsw", "'Xt") \ | |
779 V(LDRSB_w, "ldrsb", "'Wt") \ | |
780 V(LDRSH_w, "ldrsh", "'Wt") \ | |
781 V(STR_s, "str", "'St") \ | |
782 V(STR_d, "str", "'Dt") \ | |
783 V(LDR_s, "ldr", "'St") \ | |
784 V(LDR_d, "ldr", "'Dt") | |
785 | |
786 void Disassembler::VisitLoadStorePreIndex(Instruction* instr) { | |
787 const char *mnemonic = "unimplemented"; | |
788 const char *form = "(LoadStorePreIndex)"; | |
789 | |
790 switch (instr->Mask(LoadStorePreIndexMask)) { | |
791 #define LS_PREINDEX(A, B, C) \ | |
792 case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; | |
793 LOAD_STORE_LIST(LS_PREINDEX) | |
794 #undef LS_PREINDEX | |
795 } | |
796 Format(instr, mnemonic, form); | |
797 } | |
798 | |
799 | |
800 void Disassembler::VisitLoadStorePostIndex(Instruction* instr) { | |
801 const char *mnemonic = "unimplemented"; | |
802 const char *form = "(LoadStorePostIndex)"; | |
803 | |
804 switch (instr->Mask(LoadStorePostIndexMask)) { | |
805 #define LS_POSTINDEX(A, B, C) \ | |
806 case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; | |
807 LOAD_STORE_LIST(LS_POSTINDEX) | |
808 #undef LS_POSTINDEX | |
809 } | |
810 Format(instr, mnemonic, form); | |
811 } | |
812 | |
813 | |
814 void Disassembler::VisitLoadStoreUnsignedOffset(Instruction* instr) { | |
815 const char *mnemonic = "unimplemented"; | |
816 const char *form = "(LoadStoreUnsignedOffset)"; | |
817 | |
818 switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { | |
819 #define LS_UNSIGNEDOFFSET(A, B, C) \ | |
820 case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; | |
821 LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) | |
822 #undef LS_UNSIGNEDOFFSET | |
823 case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xn'ILU]"; | |
824 } | |
825 Format(instr, mnemonic, form); | |
826 } | |
827 | |
828 | |
829 void Disassembler::VisitLoadStoreRegisterOffset(Instruction* instr) { | |
830 const char *mnemonic = "unimplemented"; | |
831 const char *form = "(LoadStoreRegisterOffset)"; | |
832 | |
833 switch (instr->Mask(LoadStoreRegisterOffsetMask)) { | |
834 #define LS_REGISTEROFFSET(A, B, C) \ | |
835 case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; | |
836 LOAD_STORE_LIST(LS_REGISTEROFFSET) | |
837 #undef LS_REGISTEROFFSET | |
838 case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; | |
839 } | |
840 Format(instr, mnemonic, form); | |
841 } | |
842 | |
843 | |
844 void Disassembler::VisitLoadStoreUnscaledOffset(Instruction* instr) { | |
845 const char *mnemonic = "unimplemented"; | |
846 const char *form = "'Wt, ['Xns'ILS]"; | |
847 const char *form_x = "'Xt, ['Xns'ILS]"; | |
848 const char *form_s = "'St, ['Xns'ILS]"; | |
849 const char *form_d = "'Dt, ['Xns'ILS]"; | |
850 | |
851 switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { | |
852 case STURB_w: mnemonic = "sturb"; break; | |
853 case STURH_w: mnemonic = "sturh"; break; | |
854 case STUR_w: mnemonic = "stur"; break; | |
855 case STUR_x: mnemonic = "stur"; form = form_x; break; | |
856 case STUR_s: mnemonic = "stur"; form = form_s; break; | |
857 case STUR_d: mnemonic = "stur"; form = form_d; break; | |
858 case LDURB_w: mnemonic = "ldurb"; break; | |
859 case LDURH_w: mnemonic = "ldurh"; break; | |
860 case LDUR_w: mnemonic = "ldur"; break; | |
861 case LDUR_x: mnemonic = "ldur"; form = form_x; break; | |
862 case LDUR_s: mnemonic = "ldur"; form = form_s; break; | |
863 case LDUR_d: mnemonic = "ldur"; form = form_d; break; | |
864 case LDURSB_x: form = form_x; // Fall through. | |
865 case LDURSB_w: mnemonic = "ldursb"; break; | |
866 case LDURSH_x: form = form_x; // Fall through. | |
867 case LDURSH_w: mnemonic = "ldursh"; break; | |
868 case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; | |
869 default: form = "(LoadStoreUnscaledOffset)"; | |
870 } | |
871 Format(instr, mnemonic, form); | |
872 } | |
873 | |
874 | |
875 void Disassembler::VisitLoadLiteral(Instruction* instr) { | |
876 const char *mnemonic = "ldr"; | |
877 const char *form = "(LoadLiteral)"; | |
878 | |
879 switch (instr->Mask(LoadLiteralMask)) { | |
880 case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; | |
881 case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; | |
882 case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; | |
883 case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; | |
884 default: mnemonic = "unimplemented"; | |
885 } | |
886 Format(instr, mnemonic, form); | |
887 } | |
888 | |
889 | |
890 #define LOAD_STORE_PAIR_LIST(V) \ | |
891 V(STP_w, "stp", "'Wt, 'Wt2", "4") \ | |
892 V(LDP_w, "ldp", "'Wt, 'Wt2", "4") \ | |
893 V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "4") \ | |
894 V(STP_x, "stp", "'Xt, 'Xt2", "8") \ | |
895 V(LDP_x, "ldp", "'Xt, 'Xt2", "8") \ | |
896 V(STP_s, "stp", "'St, 'St2", "4") \ | |
897 V(LDP_s, "ldp", "'St, 'St2", "4") \ | |
898 V(STP_d, "stp", "'Dt, 'Dt2", "8") \ | |
899 V(LDP_d, "ldp", "'Dt, 'Dt2", "8") | |
900 | |
901 void Disassembler::VisitLoadStorePairPostIndex(Instruction* instr) { | |
902 const char *mnemonic = "unimplemented"; | |
903 const char *form = "(LoadStorePairPostIndex)"; | |
904 | |
905 switch (instr->Mask(LoadStorePairPostIndexMask)) { | |
906 #define LSP_POSTINDEX(A, B, C, D) \ | |
907 case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; | |
908 LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) | |
909 #undef LSP_POSTINDEX | |
910 } | |
911 Format(instr, mnemonic, form); | |
912 } | |
913 | |
914 | |
915 void Disassembler::VisitLoadStorePairPreIndex(Instruction* instr) { | |
916 const char *mnemonic = "unimplemented"; | |
917 const char *form = "(LoadStorePairPreIndex)"; | |
918 | |
919 switch (instr->Mask(LoadStorePairPreIndexMask)) { | |
920 #define LSP_PREINDEX(A, B, C, D) \ | |
921 case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; | |
922 LOAD_STORE_PAIR_LIST(LSP_PREINDEX) | |
923 #undef LSP_PREINDEX | |
924 } | |
925 Format(instr, mnemonic, form); | |
926 } | |
927 | |
928 | |
929 void Disassembler::VisitLoadStorePairOffset(Instruction* instr) { | |
930 const char *mnemonic = "unimplemented"; | |
931 const char *form = "(LoadStorePairOffset)"; | |
932 | |
933 switch (instr->Mask(LoadStorePairOffsetMask)) { | |
934 #define LSP_OFFSET(A, B, C, D) \ | |
935 case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; | |
936 LOAD_STORE_PAIR_LIST(LSP_OFFSET) | |
937 #undef LSP_OFFSET | |
938 } | |
939 Format(instr, mnemonic, form); | |
940 } | |
941 | |
942 | |
943 void Disassembler::VisitLoadStorePairNonTemporal(Instruction* instr) { | |
944 const char *mnemonic = "unimplemented"; | |
945 const char *form; | |
946 | |
947 switch (instr->Mask(LoadStorePairNonTemporalMask)) { | |
948 case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; | |
949 case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; | |
950 case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; | |
951 case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; | |
952 case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; | |
953 case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; | |
954 case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; | |
955 case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; | |
956 default: form = "(LoadStorePairNonTemporal)"; | |
957 } | |
958 Format(instr, mnemonic, form); | |
959 } | |
960 | |
961 | |
962 void Disassembler::VisitFPCompare(Instruction* instr) { | |
963 const char *mnemonic = "unimplemented"; | |
964 const char *form = "'Fn, 'Fm"; | |
965 const char *form_zero = "'Fn, #0.0"; | |
966 | |
967 switch (instr->Mask(FPCompareMask)) { | |
968 case FCMP_s_zero: | |
969 case FCMP_d_zero: form = form_zero; // Fall through. | |
970 case FCMP_s: | |
971 case FCMP_d: mnemonic = "fcmp"; break; | |
972 default: form = "(FPCompare)"; | |
973 } | |
974 Format(instr, mnemonic, form); | |
975 } | |
976 | |
977 | |
978 void Disassembler::VisitFPConditionalCompare(Instruction* instr) { | |
979 const char *mnemonic = "unimplemented"; | |
980 const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; | |
981 | |
982 switch (instr->Mask(FPConditionalCompareMask)) { | |
983 case FCCMP_s: | |
984 case FCCMP_d: mnemonic = "fccmp"; break; | |
985 case FCCMPE_s: | |
986 case FCCMPE_d: mnemonic = "fccmpe"; break; | |
987 default: form = "(FPConditionalCompare)"; | |
988 } | |
989 Format(instr, mnemonic, form); | |
990 } | |
991 | |
992 | |
993 void Disassembler::VisitFPConditionalSelect(Instruction* instr) { | |
994 const char *mnemonic = ""; | |
995 const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; | |
996 | |
997 switch (instr->Mask(FPConditionalSelectMask)) { | |
998 case FCSEL_s: | |
999 case FCSEL_d: mnemonic = "fcsel"; break; | |
1000 default: UNREACHABLE(); | |
1001 } | |
1002 Format(instr, mnemonic, form); | |
1003 } | |
1004 | |
1005 | |
1006 void Disassembler::VisitFPDataProcessing1Source(Instruction* instr) { | |
1007 const char *mnemonic = "unimplemented"; | |
1008 const char *form = "'Fd, 'Fn"; | |
1009 | |
1010 switch (instr->Mask(FPDataProcessing1SourceMask)) { | |
1011 #define FORMAT(A, B) \ | |
1012 case A##_s: \ | |
1013 case A##_d: mnemonic = B; break; | |
1014 FORMAT(FMOV, "fmov"); | |
1015 FORMAT(FABS, "fabs"); | |
1016 FORMAT(FNEG, "fneg"); | |
1017 FORMAT(FSQRT, "fsqrt"); | |
1018 FORMAT(FRINTN, "frintn"); | |
1019 FORMAT(FRINTP, "frintp"); | |
1020 FORMAT(FRINTM, "frintm"); | |
1021 FORMAT(FRINTZ, "frintz"); | |
1022 FORMAT(FRINTA, "frinta"); | |
1023 FORMAT(FRINTX, "frintx"); | |
1024 FORMAT(FRINTI, "frinti"); | |
1025 #undef FORMAT | |
1026 case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; | |
1027 case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; | |
1028 default: form = "(FPDataProcessing1Source)"; | |
1029 } | |
1030 Format(instr, mnemonic, form); | |
1031 } | |
1032 | |
1033 | |
1034 void Disassembler::VisitFPDataProcessing2Source(Instruction* instr) { | |
1035 const char *mnemonic = ""; | |
1036 const char *form = "'Fd, 'Fn, 'Fm"; | |
1037 | |
1038 switch (instr->Mask(FPDataProcessing2SourceMask)) { | |
1039 #define FORMAT(A, B) \ | |
1040 case A##_s: \ | |
1041 case A##_d: mnemonic = B; break; | |
1042 FORMAT(FMUL, "fmul"); | |
1043 FORMAT(FDIV, "fdiv"); | |
1044 FORMAT(FADD, "fadd"); | |
1045 FORMAT(FSUB, "fsub"); | |
1046 FORMAT(FMAX, "fmax"); | |
1047 FORMAT(FMIN, "fmin"); | |
1048 FORMAT(FMAXNM, "fmaxnm"); | |
1049 FORMAT(FMINNM, "fminnm"); | |
1050 FORMAT(FNMUL, "fnmul"); | |
1051 #undef FORMAT | |
1052 default: UNREACHABLE(); | |
1053 } | |
1054 Format(instr, mnemonic, form); | |
1055 } | |
1056 | |
1057 | |
1058 void Disassembler::VisitFPDataProcessing3Source(Instruction* instr) { | |
1059 const char *mnemonic = ""; | |
1060 const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; | |
1061 | |
1062 switch (instr->Mask(FPDataProcessing3SourceMask)) { | |
1063 #define FORMAT(A, B) \ | |
1064 case A##_s: \ | |
1065 case A##_d: mnemonic = B; break; | |
1066 FORMAT(FMADD, "fmadd"); | |
1067 FORMAT(FMSUB, "fmsub"); | |
1068 FORMAT(FNMADD, "fnmadd"); | |
1069 FORMAT(FNMSUB, "fnmsub"); | |
1070 #undef FORMAT | |
1071 default: UNREACHABLE(); | |
1072 } | |
1073 Format(instr, mnemonic, form); | |
1074 } | |
1075 | |
1076 | |
1077 void Disassembler::VisitFPImmediate(Instruction* instr) { | |
1078 const char *mnemonic = ""; | |
1079 const char *form = "(FPImmediate)"; | |
1080 | |
1081 switch (instr->Mask(FPImmediateMask)) { | |
1082 case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; | |
1083 case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; | |
1084 default: UNREACHABLE(); | |
1085 } | |
1086 Format(instr, mnemonic, form); | |
1087 } | |
1088 | |
1089 | |
1090 void Disassembler::VisitFPIntegerConvert(Instruction* instr) { | |
1091 const char *mnemonic = "unimplemented"; | |
1092 const char *form = "(FPIntegerConvert)"; | |
1093 const char *form_rf = "'Rd, 'Fn"; | |
1094 const char *form_fr = "'Fd, 'Rn"; | |
1095 | |
1096 switch (instr->Mask(FPIntegerConvertMask)) { | |
1097 case FMOV_ws: | |
1098 case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; | |
1099 case FMOV_sw: | |
1100 case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; | |
1101 case FCVTAS_ws: | |
1102 case FCVTAS_xs: | |
1103 case FCVTAS_wd: | |
1104 case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; | |
1105 case FCVTAU_ws: | |
1106 case FCVTAU_xs: | |
1107 case FCVTAU_wd: | |
1108 case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; | |
1109 case FCVTMS_ws: | |
1110 case FCVTMS_xs: | |
1111 case FCVTMS_wd: | |
1112 case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; | |
1113 case FCVTMU_ws: | |
1114 case FCVTMU_xs: | |
1115 case FCVTMU_wd: | |
1116 case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; | |
1117 case FCVTNS_ws: | |
1118 case FCVTNS_xs: | |
1119 case FCVTNS_wd: | |
1120 case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; | |
1121 case FCVTNU_ws: | |
1122 case FCVTNU_xs: | |
1123 case FCVTNU_wd: | |
1124 case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; | |
1125 case FCVTZU_xd: | |
1126 case FCVTZU_ws: | |
1127 case FCVTZU_wd: | |
1128 case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; | |
1129 case FCVTZS_xd: | |
1130 case FCVTZS_wd: | |
1131 case FCVTZS_xs: | |
1132 case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; | |
1133 case SCVTF_sw: | |
1134 case SCVTF_sx: | |
1135 case SCVTF_dw: | |
1136 case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; | |
1137 case UCVTF_sw: | |
1138 case UCVTF_sx: | |
1139 case UCVTF_dw: | |
1140 case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; | |
1141 } | |
1142 Format(instr, mnemonic, form); | |
1143 } | |
1144 | |
1145 | |
1146 void Disassembler::VisitFPFixedPointConvert(Instruction* instr) { | |
1147 const char *mnemonic = ""; | |
1148 const char *form = "'Rd, 'Fn, 'IFPFBits"; | |
1149 const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; | |
1150 | |
1151 switch (instr->Mask(FPFixedPointConvertMask)) { | |
1152 case FCVTZS_ws_fixed: | |
1153 case FCVTZS_xs_fixed: | |
1154 case FCVTZS_wd_fixed: | |
1155 case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; | |
1156 case FCVTZU_ws_fixed: | |
1157 case FCVTZU_xs_fixed: | |
1158 case FCVTZU_wd_fixed: | |
1159 case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; | |
1160 case SCVTF_sw_fixed: | |
1161 case SCVTF_sx_fixed: | |
1162 case SCVTF_dw_fixed: | |
1163 case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; | |
1164 case UCVTF_sw_fixed: | |
1165 case UCVTF_sx_fixed: | |
1166 case UCVTF_dw_fixed: | |
1167 case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; | |
1168 } | |
1169 Format(instr, mnemonic, form); | |
1170 } | |
1171 | |
1172 | |
1173 void Disassembler::VisitSystem(Instruction* instr) { | |
1174 // Some system instructions hijack their Op and Cp fields to represent a | |
1175 // range of immediates instead of indicating a different instruction. This | |
1176 // makes the decoding tricky. | |
1177 const char *mnemonic = "unimplemented"; | |
1178 const char *form = "(System)"; | |
1179 | |
1180 if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { | |
1181 switch (instr->Mask(SystemSysRegMask)) { | |
1182 case MRS: { | |
1183 mnemonic = "mrs"; | |
1184 switch (instr->ImmSystemRegister()) { | |
1185 case NZCV: form = "'Xt, nzcv"; break; | |
1186 case FPCR: form = "'Xt, fpcr"; break; | |
1187 default: form = "'Xt, (unknown)"; break; | |
1188 } | |
1189 break; | |
1190 } | |
1191 case MSR: { | |
1192 mnemonic = "msr"; | |
1193 switch (instr->ImmSystemRegister()) { | |
1194 case NZCV: form = "nzcv, 'Xt"; break; | |
1195 case FPCR: form = "fpcr, 'Xt"; break; | |
1196 default: form = "(unknown), 'Xt"; break; | |
1197 } | |
1198 break; | |
1199 } | |
1200 } | |
1201 } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { | |
1202 ASSERT(instr->Mask(SystemHintMask) == HINT); | |
1203 switch (instr->ImmHint()) { | |
1204 case NOP: { | |
1205 mnemonic = "nop"; | |
1206 form = NULL; | |
1207 break; | |
1208 } | |
1209 } | |
1210 } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { | |
1211 switch (instr->Mask(MemBarrierMask)) { | |
1212 case DMB: { | |
1213 mnemonic = "dmb"; | |
1214 form = "'M"; | |
1215 break; | |
1216 } | |
1217 case DSB: { | |
1218 mnemonic = "dsb"; | |
1219 form = "'M"; | |
1220 break; | |
1221 } | |
1222 case ISB: { | |
1223 mnemonic = "isb"; | |
1224 form = NULL; | |
1225 break; | |
1226 } | |
1227 } | |
1228 } | |
1229 | |
1230 Format(instr, mnemonic, form); | |
1231 } | |
1232 | |
1233 | |
1234 void Disassembler::VisitException(Instruction* instr) { | |
1235 const char *mnemonic = "unimplemented"; | |
1236 const char *form = "'IDebug"; | |
1237 | |
1238 switch (instr->Mask(ExceptionMask)) { | |
1239 case HLT: mnemonic = "hlt"; break; | |
1240 case BRK: mnemonic = "brk"; break; | |
1241 case SVC: mnemonic = "svc"; break; | |
1242 case HVC: mnemonic = "hvc"; break; | |
1243 case SMC: mnemonic = "smc"; break; | |
1244 case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; | |
1245 case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; | |
1246 case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; | |
1247 default: form = "(Exception)"; | |
1248 } | |
1249 Format(instr, mnemonic, form); | |
1250 } | |
1251 | |
1252 | |
1253 void Disassembler::VisitUnimplemented(Instruction* instr) { | |
1254 Format(instr, "unimplemented", "(Unimplemented)"); | |
1255 } | |
1256 | |
1257 | |
1258 void Disassembler::VisitUnallocated(Instruction* instr) { | |
1259 Format(instr, "unallocated", "(Unallocated)"); | |
1260 } | |
1261 | |
1262 | |
1263 void Disassembler::ProcessOutput(Instruction* /*instr*/) { | |
1264 // The base disasm does nothing more than disassembling into a buffer. | |
1265 } | |
1266 | |
1267 | |
1268 void Disassembler::Format(Instruction* instr, const char* mnemonic, | |
1269 const char* format) { | |
1270 // TODO(mcapewel) don't think I can use the instr address here - there needs | |
1271 // to be a base address too | |
1272 ASSERT(mnemonic != NULL); | |
1273 ResetOutput(); | |
1274 Substitute(instr, mnemonic); | |
1275 if (format != NULL) { | |
1276 buffer_[buffer_pos_++] = ' '; | |
1277 Substitute(instr, format); | |
1278 } | |
1279 buffer_[buffer_pos_] = 0; | |
1280 ProcessOutput(instr); | |
1281 } | |
1282 | |
1283 | |
1284 void Disassembler::Substitute(Instruction* instr, const char* string) { | |
1285 char chr = *string++; | |
1286 while (chr != '\0') { | |
1287 if (chr == '\'') { | |
1288 string += SubstituteField(instr, string); | |
1289 } else { | |
1290 buffer_[buffer_pos_++] = chr; | |
1291 } | |
1292 chr = *string++; | |
1293 } | |
1294 } | |
1295 | |
1296 | |
1297 int Disassembler::SubstituteField(Instruction* instr, const char* format) { | |
1298 switch (format[0]) { | |
1299 case 'R': // Register. X or W, selected by sf bit. | |
1300 case 'F': // FP Register. S or D, selected by type field. | |
1301 case 'W': | |
1302 case 'X': | |
1303 case 'S': | |
1304 case 'D': return SubstituteRegisterField(instr, format); | |
1305 case 'I': return SubstituteImmediateField(instr, format); | |
1306 case 'L': return SubstituteLiteralField(instr, format); | |
1307 case 'H': return SubstituteShiftField(instr, format); | |
1308 case 'P': return SubstitutePrefetchField(instr, format); | |
1309 case 'C': return SubstituteConditionField(instr, format); | |
1310 case 'E': return SubstituteExtendField(instr, format); | |
1311 case 'A': return SubstitutePCRelAddressField(instr, format); | |
1312 case 'B': return SubstituteBranchTargetField(instr, format); | |
1313 case 'O': return SubstituteLSRegOffsetField(instr, format); | |
1314 case 'M': return SubstituteBarrierField(instr, format); | |
1315 default: { | |
1316 UNREACHABLE(); | |
1317 return 1; | |
1318 } | |
1319 } | |
1320 } | |
1321 | |
1322 | |
1323 int Disassembler::SubstituteRegisterField(Instruction* instr, | |
1324 const char* format) { | |
1325 unsigned reg_num = 0; | |
1326 unsigned field_len = 2; | |
1327 switch (format[1]) { | |
1328 case 'd': reg_num = instr->Rd(); break; | |
1329 case 'n': reg_num = instr->Rn(); break; | |
1330 case 'm': reg_num = instr->Rm(); break; | |
1331 case 'a': reg_num = instr->Ra(); break; | |
1332 case 't': { | |
1333 if (format[2] == '2') { | |
1334 reg_num = instr->Rt2(); | |
1335 field_len = 3; | |
1336 } else { | |
1337 reg_num = instr->Rt(); | |
1338 } | |
1339 break; | |
1340 } | |
1341 default: UNREACHABLE(); | |
1342 } | |
1343 | |
1344 // Increase field length for registers tagged as stack. | |
1345 if (format[2] == 's') { | |
1346 field_len = 3; | |
1347 } | |
1348 | |
1349 char reg_type; | |
1350 if (format[0] == 'R') { | |
1351 // Register type is R: use sf bit to choose X and W. | |
1352 reg_type = instr->SixtyFourBits() ? 'x' : 'w'; | |
1353 } else if (format[0] == 'F') { | |
1354 // Floating-point register: use type field to choose S or D. | |
1355 reg_type = ((instr->FPType() & 1) == 0) ? 's' : 'd'; | |
1356 } else { | |
1357 // Register type is specified. Make it lower case. | |
1358 reg_type = format[0] + 0x20; | |
1359 } | |
1360 | |
1361 if ((reg_num != kZeroRegCode) || (reg_type == 's') || (reg_type == 'd')) { | |
1362 // A normal register: w0 - w30, x0 - x30, s0 - s31, d0 - d31. | |
1363 | |
1364 // Filter special registers | |
1365 if ((reg_type == 'x') && (reg_num == 27)) { | |
1366 AppendToOutput("cp"); | |
1367 } else if ((reg_type == 'x') && (reg_num == 28)) { | |
1368 AppendToOutput("jssp"); | |
1369 } else if ((reg_type == 'x') && (reg_num == 29)) { | |
1370 AppendToOutput("fp"); | |
1371 } else if ((reg_type == 'x') && (reg_num == 30)) { | |
1372 AppendToOutput("lr"); | |
1373 } else { | |
1374 AppendToOutput("%c%d", reg_type, reg_num); | |
1375 } | |
1376 } else if (format[2] == 's') { | |
1377 // Disassemble w31/x31 as stack pointer wcsp/csp. | |
1378 AppendToOutput("%s", (reg_type == 'w') ? "wcsp" : "csp"); | |
1379 } else { | |
1380 // Disassemble w31/x31 as zero register wzr/xzr. | |
1381 AppendToOutput("%czr", reg_type); | |
1382 } | |
1383 | |
1384 return field_len; | |
1385 } | |
1386 | |
1387 | |
1388 int Disassembler::SubstituteImmediateField(Instruction* instr, | |
1389 const char* format) { | |
1390 ASSERT(format[0] == 'I'); | |
1391 | |
1392 switch (format[1]) { | |
1393 case 'M': { // IMoveImm or IMoveLSL. | |
1394 if (format[5] == 'I') { | |
1395 uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); | |
1396 AppendToOutput("#0x%" PRIx64, imm); | |
1397 } else { | |
1398 ASSERT(format[5] == 'L'); | |
1399 AppendToOutput("#0x%" PRIx64, instr->ImmMoveWide()); | |
1400 if (instr->ShiftMoveWide() > 0) { | |
1401 AppendToOutput(", lsl #%d", 16 * instr->ShiftMoveWide()); | |
1402 } | |
1403 } | |
1404 return 8; | |
1405 } | |
1406 case 'L': { | |
1407 switch (format[2]) { | |
1408 case 'L': { // ILLiteral - Immediate Load Literal. | |
1409 AppendToOutput("pc%+" PRId64, | |
1410 instr->ImmLLiteral() << kLiteralEntrySizeLog2); | |
1411 return 9; | |
1412 } | |
1413 case 'S': { // ILS - Immediate Load/Store. | |
1414 if (instr->ImmLS() != 0) { | |
1415 AppendToOutput(", #%" PRId64, instr->ImmLS()); | |
1416 } | |
1417 return 3; | |
1418 } | |
1419 case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. | |
1420 if (instr->ImmLSPair() != 0) { | |
1421 // format[3] is the scale value. Convert to a number. | |
1422 int scale = format[3] - 0x30; | |
1423 AppendToOutput(", #%" PRId64, instr->ImmLSPair() * scale); | |
1424 } | |
1425 return 4; | |
1426 } | |
1427 case 'U': { // ILU - Immediate Load/Store Unsigned. | |
1428 if (instr->ImmLSUnsigned() != 0) { | |
1429 AppendToOutput(", #%" PRIu64, | |
1430 instr->ImmLSUnsigned() << instr->SizeLS()); | |
1431 } | |
1432 return 3; | |
1433 } | |
1434 } | |
1435 } | |
1436 case 'C': { // ICondB - Immediate Conditional Branch. | |
1437 int64_t offset = instr->ImmCondBranch() << 2; | |
1438 char sign = (offset >= 0) ? '+' : '-'; | |
1439 AppendToOutput("#%c0x%" PRIx64, sign, offset); | |
1440 return 6; | |
1441 } | |
1442 case 'A': { // IAddSub. | |
1443 ASSERT(instr->ShiftAddSub() <= 1); | |
1444 int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); | |
1445 AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); | |
1446 return 7; | |
1447 } | |
1448 case 'F': { // IFPSingle, IFPDouble or IFPFBits. | |
1449 if (format[3] == 'F') { // IFPFBits. | |
1450 AppendToOutput("#%d", 64 - instr->FPScale()); | |
1451 return 8; | |
1452 } else { | |
1453 AppendToOutput("#0x%" PRIx64 " (%.4f)", instr->ImmFP(), | |
1454 format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); | |
1455 return 9; | |
1456 } | |
1457 } | |
1458 case 'T': { // ITri - Immediate Triangular Encoded. | |
1459 AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); | |
1460 return 4; | |
1461 } | |
1462 case 'N': { // INzcv. | |
1463 int nzcv = (instr->Nzcv() << Flags_offset); | |
1464 AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', | |
1465 ((nzcv & ZFlag) == 0) ? 'z' : 'Z', | |
1466 ((nzcv & CFlag) == 0) ? 'c' : 'C', | |
1467 ((nzcv & VFlag) == 0) ? 'v' : 'V'); | |
1468 return 5; | |
1469 } | |
1470 case 'P': { // IP - Conditional compare. | |
1471 AppendToOutput("#%d", instr->ImmCondCmp()); | |
1472 return 2; | |
1473 } | |
1474 case 'B': { // Bitfields. | |
1475 return SubstituteBitfieldImmediateField(instr, format); | |
1476 } | |
1477 case 'E': { // IExtract. | |
1478 AppendToOutput("#%d", instr->ImmS()); | |
1479 return 8; | |
1480 } | |
1481 case 'S': { // IS - Test and branch bit. | |
1482 AppendToOutput("#%d", (instr->ImmTestBranchBit5() << 5) | | |
1483 instr->ImmTestBranchBit40()); | |
1484 return 2; | |
1485 } | |
1486 case 'D': { // IDebug - HLT and BRK instructions. | |
1487 AppendToOutput("#0x%x", instr->ImmException()); | |
1488 return 6; | |
1489 } | |
1490 default: { | |
1491 UNREACHABLE(); | |
1492 return 0; | |
1493 } | |
1494 } | |
1495 } | |
1496 | |
1497 | |
1498 int Disassembler::SubstituteBitfieldImmediateField(Instruction* instr, | |
1499 const char* format) { | |
1500 ASSERT((format[0] == 'I') && (format[1] == 'B')); | |
1501 unsigned r = instr->ImmR(); | |
1502 unsigned s = instr->ImmS(); | |
1503 | |
1504 switch (format[2]) { | |
1505 case 'r': { // IBr. | |
1506 AppendToOutput("#%d", r); | |
1507 return 3; | |
1508 } | |
1509 case 's': { // IBs+1 or IBs-r+1. | |
1510 if (format[3] == '+') { | |
1511 AppendToOutput("#%d", s + 1); | |
1512 return 5; | |
1513 } else { | |
1514 ASSERT(format[3] == '-'); | |
1515 AppendToOutput("#%d", s - r + 1); | |
1516 return 7; | |
1517 } | |
1518 } | |
1519 case 'Z': { // IBZ-r. | |
1520 ASSERT((format[3] == '-') && (format[4] == 'r')); | |
1521 unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSizeInBits | |
1522 : kWRegSizeInBits; | |
1523 AppendToOutput("#%d", reg_size - r); | |
1524 return 5; | |
1525 } | |
1526 default: { | |
1527 UNREACHABLE(); | |
1528 return 0; | |
1529 } | |
1530 } | |
1531 } | |
1532 | |
1533 | |
1534 int Disassembler::SubstituteLiteralField(Instruction* instr, | |
1535 const char* format) { | |
1536 ASSERT(strncmp(format, "LValue", 6) == 0); | |
1537 USE(format); | |
1538 | |
1539 switch (instr->Mask(LoadLiteralMask)) { | |
1540 case LDR_w_lit: | |
1541 case LDR_x_lit: | |
1542 case LDR_s_lit: | |
1543 case LDR_d_lit: AppendToOutput("(addr %p)", instr->LiteralAddress()); break; | |
1544 default: UNREACHABLE(); | |
1545 } | |
1546 | |
1547 return 6; | |
1548 } | |
1549 | |
1550 | |
1551 int Disassembler::SubstituteShiftField(Instruction* instr, const char* format) { | |
1552 ASSERT(format[0] == 'H'); | |
1553 ASSERT(instr->ShiftDP() <= 0x3); | |
1554 | |
1555 switch (format[1]) { | |
1556 case 'D': { // HDP. | |
1557 ASSERT(instr->ShiftDP() != ROR); | |
1558 } // Fall through. | |
1559 case 'L': { // HLo. | |
1560 if (instr->ImmDPShift() != 0) { | |
1561 const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; | |
1562 AppendToOutput(", %s #%" PRId64, shift_type[instr->ShiftDP()], | |
1563 instr->ImmDPShift()); | |
1564 } | |
1565 return 3; | |
1566 } | |
1567 default: | |
1568 UNREACHABLE(); | |
1569 return 0; | |
1570 } | |
1571 } | |
1572 | |
1573 | |
1574 int Disassembler::SubstituteConditionField(Instruction* instr, | |
1575 const char* format) { | |
1576 ASSERT(format[0] == 'C'); | |
1577 const char* condition_code[] = { "eq", "ne", "hs", "lo", | |
1578 "mi", "pl", "vs", "vc", | |
1579 "hi", "ls", "ge", "lt", | |
1580 "gt", "le", "al", "nv" }; | |
1581 int cond; | |
1582 switch (format[1]) { | |
1583 case 'B': cond = instr->ConditionBranch(); break; | |
1584 case 'I': { | |
1585 cond = InvertCondition(static_cast<Condition>(instr->Condition())); | |
1586 break; | |
1587 } | |
1588 default: cond = instr->Condition(); | |
1589 } | |
1590 AppendToOutput("%s", condition_code[cond]); | |
1591 return 4; | |
1592 } | |
1593 | |
1594 | |
1595 int Disassembler::SubstitutePCRelAddressField(Instruction* instr, | |
1596 const char* format) { | |
1597 USE(format); | |
1598 ASSERT(strncmp(format, "AddrPCRel", 9) == 0); | |
1599 | |
1600 int offset = instr->ImmPCRel(); | |
1601 | |
1602 // Only ADR (AddrPCRelByte) is supported. | |
1603 ASSERT(strcmp(format, "AddrPCRelByte") == 0); | |
1604 | |
1605 char sign = '+'; | |
1606 if (offset < 0) { | |
1607 offset = -offset; | |
1608 sign = '-'; | |
1609 } | |
1610 AppendToOutput("#%c0x%x (addr %p)", sign, offset, | |
1611 instr->InstructionAtOffset(offset, Instruction::NO_CHECK)); | |
1612 return 13; | |
1613 } | |
1614 | |
1615 | |
1616 int Disassembler::SubstituteBranchTargetField(Instruction* instr, | |
1617 const char* format) { | |
1618 ASSERT(strncmp(format, "BImm", 4) == 0); | |
1619 | |
1620 int64_t offset = 0; | |
1621 switch (format[5]) { | |
1622 // BImmUncn - unconditional branch immediate. | |
1623 case 'n': offset = instr->ImmUncondBranch(); break; | |
1624 // BImmCond - conditional branch immediate. | |
1625 case 'o': offset = instr->ImmCondBranch(); break; | |
1626 // BImmCmpa - compare and branch immediate. | |
1627 case 'm': offset = instr->ImmCmpBranch(); break; | |
1628 // BImmTest - test and branch immediate. | |
1629 case 'e': offset = instr->ImmTestBranch(); break; | |
1630 default: UNREACHABLE(); | |
1631 } | |
1632 offset <<= kInstructionSizeLog2; | |
1633 char sign = '+'; | |
1634 if (offset < 0) { | |
1635 offset = -offset; | |
1636 sign = '-'; | |
1637 } | |
1638 AppendToOutput("#%c0x%" PRIx64 " (addr %p)", sign, offset, | |
1639 instr->InstructionAtOffset(offset), Instruction::NO_CHECK); | |
1640 return 8; | |
1641 } | |
1642 | |
1643 | |
1644 int Disassembler::SubstituteExtendField(Instruction* instr, | |
1645 const char* format) { | |
1646 ASSERT(strncmp(format, "Ext", 3) == 0); | |
1647 ASSERT(instr->ExtendMode() <= 7); | |
1648 USE(format); | |
1649 | |
1650 const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", | |
1651 "sxtb", "sxth", "sxtw", "sxtx" }; | |
1652 | |
1653 // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit | |
1654 // registers becomes lsl. | |
1655 if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && | |
1656 (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || | |
1657 (instr->ExtendMode() == UXTX))) { | |
1658 if (instr->ImmExtendShift() > 0) { | |
1659 AppendToOutput(", lsl #%d", instr->ImmExtendShift()); | |
1660 } | |
1661 } else { | |
1662 AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); | |
1663 if (instr->ImmExtendShift() > 0) { | |
1664 AppendToOutput(" #%d", instr->ImmExtendShift()); | |
1665 } | |
1666 } | |
1667 return 3; | |
1668 } | |
1669 | |
1670 | |
1671 int Disassembler::SubstituteLSRegOffsetField(Instruction* instr, | |
1672 const char* format) { | |
1673 ASSERT(strncmp(format, "Offsetreg", 9) == 0); | |
1674 const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", | |
1675 "undefined", "undefined", "sxtw", "sxtx" }; | |
1676 USE(format); | |
1677 | |
1678 unsigned shift = instr->ImmShiftLS(); | |
1679 Extend ext = static_cast<Extend>(instr->ExtendMode()); | |
1680 char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; | |
1681 | |
1682 unsigned rm = instr->Rm(); | |
1683 if (rm == kZeroRegCode) { | |
1684 AppendToOutput("%czr", reg_type); | |
1685 } else { | |
1686 AppendToOutput("%c%d", reg_type, rm); | |
1687 } | |
1688 | |
1689 // Extend mode UXTX is an alias for shift mode LSL here. | |
1690 if (!((ext == UXTX) && (shift == 0))) { | |
1691 AppendToOutput(", %s", extend_mode[ext]); | |
1692 if (shift != 0) { | |
1693 AppendToOutput(" #%d", instr->SizeLS()); | |
1694 } | |
1695 } | |
1696 return 9; | |
1697 } | |
1698 | |
1699 | |
1700 int Disassembler::SubstitutePrefetchField(Instruction* instr, | |
1701 const char* format) { | |
1702 ASSERT(format[0] == 'P'); | |
1703 USE(format); | |
1704 | |
1705 int prefetch_mode = instr->PrefetchMode(); | |
1706 | |
1707 const char* ls = (prefetch_mode & 0x10) ? "st" : "ld"; | |
1708 int level = (prefetch_mode >> 1) + 1; | |
1709 const char* ks = (prefetch_mode & 1) ? "strm" : "keep"; | |
1710 | |
1711 AppendToOutput("p%sl%d%s", ls, level, ks); | |
1712 return 6; | |
1713 } | |
1714 | |
1715 int Disassembler::SubstituteBarrierField(Instruction* instr, | |
1716 const char* format) { | |
1717 ASSERT(format[0] == 'M'); | |
1718 USE(format); | |
1719 | |
1720 static const char* options[4][4] = { | |
1721 { "sy (0b0000)", "oshld", "oshst", "osh" }, | |
1722 { "sy (0b0100)", "nshld", "nshst", "nsh" }, | |
1723 { "sy (0b1000)", "ishld", "ishst", "ish" }, | |
1724 { "sy (0b1100)", "ld", "st", "sy" } | |
1725 }; | |
1726 int domain = instr->ImmBarrierDomain(); | |
1727 int type = instr->ImmBarrierType(); | |
1728 | |
1729 AppendToOutput("%s", options[domain][type]); | |
1730 return 1; | |
1731 } | |
1732 | |
1733 | |
1734 void Disassembler::ResetOutput() { | |
1735 buffer_pos_ = 0; | |
1736 buffer_[buffer_pos_] = 0; | |
1737 } | |
1738 | |
1739 | |
1740 void Disassembler::AppendToOutput(const char* format, ...) { | |
1741 va_list args; | |
1742 va_start(args, format); | |
1743 buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_, format, args); | |
1744 va_end(args); | |
1745 } | |
1746 | |
1747 | |
1748 void PrintDisassembler::ProcessOutput(Instruction* instr) { | |
1749 fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", | |
1750 reinterpret_cast<uint64_t>(instr), instr->InstructionBits(), | |
1751 GetOutput()); | |
1752 } | |
1753 | |
1754 } } // namespace v8::internal | |
1755 | |
1756 | |
1757 namespace disasm { | |
1758 | |
1759 | |
1760 const char* NameConverter::NameOfAddress(byte* addr) const { | |
1761 v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr); | |
1762 return tmp_buffer_.start(); | |
1763 } | |
1764 | |
1765 | |
1766 const char* NameConverter::NameOfConstant(byte* addr) const { | |
1767 return NameOfAddress(addr); | |
1768 } | |
1769 | |
1770 | |
1771 const char* NameConverter::NameOfCPURegister(int reg) const { | |
1772 unsigned ureg = reg; // Avoid warnings about signed/unsigned comparisons. | |
1773 if (ureg >= v8::internal::kNumberOfRegisters) { | |
1774 return "noreg"; | |
1775 } | |
1776 if (ureg == v8::internal::kZeroRegCode) { | |
1777 return "xzr"; | |
1778 } | |
1779 v8::internal::OS::SNPrintF(tmp_buffer_, "x%u", ureg); | |
1780 return tmp_buffer_.start(); | |
1781 } | |
1782 | |
1783 | |
1784 const char* NameConverter::NameOfByteCPURegister(int reg) const { | |
1785 UNREACHABLE(); // A64 does not have the concept of a byte register | |
1786 return "nobytereg"; | |
1787 } | |
1788 | |
1789 | |
1790 const char* NameConverter::NameOfXMMRegister(int reg) const { | |
1791 UNREACHABLE(); // A64 does not have any XMM registers | |
1792 return "noxmmreg"; | |
1793 } | |
1794 | |
1795 | |
1796 const char* NameConverter::NameInCode(byte* addr) const { | |
1797 // The default name converter is called for unknown code, so we will not try | |
1798 // to access any memory. | |
1799 return ""; | |
1800 } | |
1801 | |
1802 | |
1803 //------------------------------------------------------------------------------ | |
1804 | |
1805 class BufferDisassembler : public v8::internal::Disassembler { | |
1806 public: | |
1807 explicit BufferDisassembler(v8::internal::Vector<char> out_buffer) | |
1808 : out_buffer_(out_buffer) { } | |
1809 | |
1810 ~BufferDisassembler() { } | |
1811 | |
1812 virtual void ProcessOutput(v8::internal::Instruction* instr) { | |
1813 v8::internal::OS::SNPrintF(out_buffer_, "%s", GetOutput()); | |
1814 } | |
1815 | |
1816 private: | |
1817 v8::internal::Vector<char> out_buffer_; | |
1818 }; | |
1819 | |
1820 Disassembler::Disassembler(const NameConverter& converter) | |
1821 : converter_(converter) {} | |
1822 | |
1823 | |
1824 Disassembler::~Disassembler() {} | |
1825 | |
1826 | |
1827 int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer, | |
1828 byte* instr) { | |
1829 v8::internal::Decoder<v8::internal::DispatchingDecoderVisitor> decoder; | |
1830 BufferDisassembler disasm(buffer); | |
1831 decoder.AppendVisitor(&disasm); | |
1832 | |
1833 decoder.Decode(reinterpret_cast<v8::internal::Instruction*>(instr)); | |
1834 return v8::internal::kInstructionSize; | |
1835 } | |
1836 | |
1837 | |
1838 int Disassembler::ConstantPoolSizeAt(byte* instr) { | |
1839 return v8::internal::Assembler::ConstantPoolSizeAt( | |
1840 reinterpret_cast<v8::internal::Instruction*>(instr)); | |
1841 } | |
1842 | |
1843 | |
1844 void Disassembler::Disassemble(FILE* file, byte* start, byte* end) { | |
1845 v8::internal::Decoder<v8::internal::DispatchingDecoderVisitor> decoder; | |
1846 v8::internal::PrintDisassembler disasm(file); | |
1847 decoder.AppendVisitor(&disasm); | |
1848 | |
1849 for (byte* pc = start; pc < end; pc += v8::internal::kInstructionSize) { | |
1850 decoder.Decode(reinterpret_cast<v8::internal::Instruction*>(pc)); | |
1851 } | |
1852 } | |
1853 | |
1854 } // namespace disasm | |
1855 | |
1856 #endif // V8_TARGET_ARCH_A64 | |
OLD | NEW |