1/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define __STDC_FORMAT_MACROS
27#include "config.h"
28
29#if USE(ARM64_DISASSEMBLER)
30
31#include "A64DOpcode.h"
32
33#include <stdarg.h>
34#include <stdint.h>
35#include <stdio.h>
36
37namespace JSC { namespace ARM64Disassembler {
38
39A64DOpcode::OpcodeGroup* A64DOpcode::opcodeTable[32];
40
41const char* const A64DOpcode::s_conditionNames[16] = {
42 "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
43 "hi", "ls", "ge", "lt", "gt", "le", "al", "ne"
44};
45
46const char* const A64DOpcode::s_optionName[8] = {
47 "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx"
48};
49
50const char* const A64DOpcode::s_shiftNames[4] = {
51 "lsl", "lsr", "asl", "ror"
52};
53
54const char A64DOpcode::s_FPRegisterPrefix[5] = {
55 'b', 'h', 's', 'd', 'q'
56};
57
58struct OpcodeGroupInitializer {
59 unsigned m_opcodeGroupNumber;
60 uint32_t m_mask;
61 uint32_t m_pattern;
62 const char* (*m_format)(A64DOpcode*);
63};
64
65#define OPCODE_GROUP_ENTRY(groupIndex, groupClass) \
66{ groupIndex, groupClass::mask, groupClass::pattern, groupClass::format }
67
68static OpcodeGroupInitializer opcodeGroupList[] = {
69 OPCODE_GROUP_ENTRY(0x08, A64DOpcodeLoadStoreRegisterPair),
70 OPCODE_GROUP_ENTRY(0x09, A64DOpcodeLoadStoreRegisterPair),
71 OPCODE_GROUP_ENTRY(0x0a, A64DOpcodeLogicalShiftedRegister),
72 OPCODE_GROUP_ENTRY(0x0b, A64DOpcodeAddSubtractExtendedRegister),
73 OPCODE_GROUP_ENTRY(0x0b, A64DOpcodeAddSubtractShiftedRegister),
74 OPCODE_GROUP_ENTRY(0x11, A64DOpcodeAddSubtractImmediate),
75 OPCODE_GROUP_ENTRY(0x12, A64DOpcodeMoveWide),
76 OPCODE_GROUP_ENTRY(0x12, A64DOpcodeLogicalImmediate),
77 OPCODE_GROUP_ENTRY(0x13, A64DOpcodeBitfield),
78 OPCODE_GROUP_ENTRY(0x13, A64DOpcodeExtract),
79 OPCODE_GROUP_ENTRY(0x14, A64DOpcodeUnconditionalBranchImmediate),
80 OPCODE_GROUP_ENTRY(0x14, A64DOpcodeConditionalBranchImmediate),
81 OPCODE_GROUP_ENTRY(0x14, A64DOpcodeCompareAndBranchImmediate),
82 OPCODE_GROUP_ENTRY(0x14, A64OpcodeExceptionGeneration),
83 OPCODE_GROUP_ENTRY(0x15, A64DOpcodeUnconditionalBranchImmediate),
84 OPCODE_GROUP_ENTRY(0x15, A64DOpcodeConditionalBranchImmediate),
85 OPCODE_GROUP_ENTRY(0x15, A64DOpcodeCompareAndBranchImmediate),
86 OPCODE_GROUP_ENTRY(0x15, A64DOpcodeHint),
87 OPCODE_GROUP_ENTRY(0x16, A64DOpcodeUnconditionalBranchImmediate),
88 OPCODE_GROUP_ENTRY(0x16, A64DOpcodeUnconditionalBranchRegister),
89 OPCODE_GROUP_ENTRY(0x16, A64DOpcodeTestAndBranchImmediate),
90 OPCODE_GROUP_ENTRY(0x17, A64DOpcodeUnconditionalBranchImmediate),
91 OPCODE_GROUP_ENTRY(0x17, A64DOpcodeUnconditionalBranchRegister),
92 OPCODE_GROUP_ENTRY(0x17, A64DOpcodeTestAndBranchImmediate),
93 OPCODE_GROUP_ENTRY(0x18, A64DOpcodeLoadStoreImmediate),
94 OPCODE_GROUP_ENTRY(0x18, A64DOpcodeLoadStoreRegisterOffset),
95 OPCODE_GROUP_ENTRY(0x19, A64DOpcodeLoadStoreUnsignedImmediate),
96 OPCODE_GROUP_ENTRY(0x1a, A64DOpcodeConditionalSelect),
97 OPCODE_GROUP_ENTRY(0x1a, A64DOpcodeDataProcessing2Source),
98 OPCODE_GROUP_ENTRY(0x1b, A64DOpcodeDataProcessing3Source),
99 OPCODE_GROUP_ENTRY(0x1c, A64DOpcodeLoadStoreImmediate),
100 OPCODE_GROUP_ENTRY(0x1c, A64DOpcodeLoadStoreRegisterOffset),
101 OPCODE_GROUP_ENTRY(0x1d, A64DOpcodeLoadStoreUnsignedImmediate),
102 OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointCompare),
103 OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointDataProcessing2Source),
104 OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointDataProcessing1Source),
105 OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingFixedPointConversions),
106 OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointIntegerConversions),
107};
108
109bool A64DOpcode::s_initialized = false;
110
111void A64DOpcode::init()
112{
113 if (s_initialized)
114 return;
115
116 OpcodeGroup* lastGroups[32];
117
118 for (unsigned i = 0; i < 32; i++) {
119 opcodeTable[i] = 0;
120 lastGroups[i] = 0;
121 }
122
123 for (unsigned i = 0; i < sizeof(opcodeGroupList) / sizeof(struct OpcodeGroupInitializer); i++) {
124 OpcodeGroup* newOpcodeGroup = new OpcodeGroup(opcodeGroupList[i].m_mask, opcodeGroupList[i].m_pattern, opcodeGroupList[i].m_format);
125 uint32_t opcodeGroupNumber = opcodeGroupList[i].m_opcodeGroupNumber;
126
127 if (!opcodeTable[opcodeGroupNumber])
128 opcodeTable[opcodeGroupNumber] = newOpcodeGroup;
129 else
130 lastGroups[opcodeGroupNumber]->setNext(newOpcodeGroup);
131 lastGroups[opcodeGroupNumber] = newOpcodeGroup;
132 }
133
134 s_initialized = true;
135}
136
137void A64DOpcode::setPCAndOpcode(uint32_t* newPC, uint32_t newOpcode)
138{
139 m_currentPC = newPC;
140 m_opcode = newOpcode;
141 m_bufferOffset = 0;
142 m_formatBuffer[0] = '\0';
143}
144
145const char* A64DOpcode::disassemble(uint32_t* currentPC)
146{
147 setPCAndOpcode(currentPC, *currentPC);
148
149 OpcodeGroup* opGroup = opcodeTable[opcodeGroupNumber(m_opcode)];
150
151 while (opGroup) {
152 if (opGroup->matches(m_opcode))
153 return opGroup->format(this);
154 opGroup = opGroup->next();
155 }
156
157 return A64DOpcode::format();
158}
159
160void A64DOpcode::bufferPrintf(const char* format, ...)
161{
162 if (m_bufferOffset >= bufferSize)
163 return;
164
165 va_list argList;
166 va_start(argList, format);
167
168 m_bufferOffset += vsnprintf(m_formatBuffer + m_bufferOffset, bufferSize - m_bufferOffset, format, argList);
169
170 va_end(argList);
171}
172
173const char* A64DOpcode::format()
174{
175 bufferPrintf(" .long %08x", m_opcode);
176 return m_formatBuffer;
177}
178
179void A64DOpcode::appendRegisterName(unsigned registerNumber, bool is64Bit)
180{
181 if (registerNumber == 29) {
182 bufferPrintf(is64Bit ? "fp" : "wfp");
183 return;
184 }
185
186 if (registerNumber == 30) {
187 bufferPrintf(is64Bit ? "lr" : "wlr");
188 return;
189 }
190
191 bufferPrintf("%c%u", is64Bit ? 'x' : 'w', registerNumber);
192}
193
194void A64DOpcode::appendFPRegisterName(unsigned registerNumber, unsigned registerSize)
195{
196 bufferPrintf("%c%u", FPRegisterPrefix(registerSize), registerNumber);
197}
198
199const char* const A64DOpcodeAddSubtract::s_opNames[4] = { "add", "adds", "sub", "subs" };
200
201const char* A64DOpcodeAddSubtractImmediate::format()
202{
203 if (isCMP())
204 appendInstructionName(cmpName());
205 else {
206 if (isMovSP())
207 appendInstructionName("mov");
208 else
209 appendInstructionName(opName());
210 appendSPOrRegisterName(rd(), is64Bit());
211 appendSeparator();
212 }
213 appendSPOrRegisterName(rn(), is64Bit());
214
215 if (!isMovSP()) {
216 appendSeparator();
217 appendUnsignedImmediate(immed12());
218 if (shift()) {
219 appendSeparator();
220 appendString(shift() == 1 ? "lsl" : "reserved");
221 }
222 }
223 return m_formatBuffer;
224}
225
226const char* A64DOpcodeAddSubtractExtendedRegister::format()
227{
228 if (immediate3() > 4)
229 return A64DOpcode::format();
230
231 if (isCMP())
232 appendInstructionName(cmpName());
233 else {
234 appendInstructionName(opName());
235 appendSPOrRegisterName(rd(), is64Bit());
236 appendSeparator();
237 }
238 appendSPOrRegisterName(rn(), is64Bit());
239 appendSeparator();
240 appendZROrRegisterName(rm(), is64Bit() && ((option() & 0x3) == 0x3));
241 appendSeparator();
242 if (option() == 0x2 && ((rd() == 31) || (rn() == 31)))
243 appendString("lsl");
244 else
245 appendString(optionName());
246 if (immediate3()) {
247 appendCharacter(' ');
248 appendUnsignedImmediate(immediate3());
249 }
250
251 return m_formatBuffer;
252}
253
254const char* A64DOpcodeAddSubtractShiftedRegister::format()
255{
256 if (!is64Bit() && immediate6() & 0x20)
257 return A64DOpcode::format();
258
259 if (shift() == 0x3)
260 return A64DOpcode::format();
261
262 if (isCMP())
263 appendInstructionName(cmpName());
264 else {
265 if (isNeg())
266 appendInstructionName(cmpName());
267 else
268 appendInstructionName(opName());
269 appendSPOrRegisterName(rd(), is64Bit());
270 appendSeparator();
271 }
272 if (!isNeg()) {
273 appendRegisterName(rn(), is64Bit());
274 appendSeparator();
275 }
276 appendZROrRegisterName(rm(), is64Bit());
277 if (immediate6()) {
278 appendSeparator();
279 appendShiftType(shift());
280 appendUnsignedImmediate(immediate6());
281 }
282
283 return m_formatBuffer;
284}
285
286const char* const A64DOpcodeBitfield::s_opNames[3] = { "sbfm", "bfm", "ubfm" };
287const char* const A64DOpcodeBitfield::s_extendPseudoOpNames[3][3] = {
288 { "sxtb", "sxth", "sxtw" }, { 0, 0, 0} , { "uxtb", "uxth", "uxtw" } };
289const char* const A64DOpcodeBitfield::s_insertOpNames[3] = { "sbfiz", "bfi", "ubfiz" };
290const char* const A64DOpcodeBitfield::s_extractOpNames[3] = { "sbfx", "bf", "ubfx" };
291
292const char* A64DOpcodeBitfield::format()
293{
294 if (opc() == 0x3)
295 return A64DOpcode::format();
296
297 if (is64Bit() != nBit())
298 return A64DOpcode::format();
299
300 if (!is64Bit() && ((immediateR() & 0x20) || (immediateS() & 0x20)))
301 return A64DOpcode::format();
302
303 if (!(opc() & 0x1) && !immediateR()) {
304 // [un]signed {btye,half-word,word} extend
305 bool isSTXType = false;
306 if (immediateS() == 7) {
307 appendInstructionName(extendPseudoOpNames(0));
308 isSTXType = true;
309 } else if (immediateS() == 15) {
310 appendInstructionName(extendPseudoOpNames(1));
311 isSTXType = true;
312 } else if (immediateS() == 31 && is64Bit()) {
313 appendInstructionName(extendPseudoOpNames(2));
314 isSTXType = true;
315 }
316
317 if (isSTXType) {
318 appendRegisterName(rd(), is64Bit());
319 appendSeparator();
320 appendRegisterName(rn(), false);
321
322 return m_formatBuffer;
323 }
324 }
325
326 if (opc() == 0x2 && immediateS() == (immediateR() + 1)) {
327 // lsl
328 appendInstructionName("lsl");
329 appendRegisterName(rd(), is64Bit());
330 appendSeparator();
331 appendRegisterName(rn(), is64Bit());
332 appendSeparator();
333 appendUnsignedImmediate((is64Bit() ? 63u : 31u) - immediateR());
334
335 return m_formatBuffer;
336 } else if (!(opc() & 0x1) && ((immediateS() & 0x1f) == 0x1f) && (is64Bit() == (immediateS() >> 5))) {
337 // asr/lsr
338 appendInstructionName(!opc() ? "ars" : "lsr");
339
340 appendRegisterName(rd(), is64Bit());
341 appendSeparator();
342 appendRegisterName(rn(), is64Bit());
343 appendSeparator();
344 appendUnsignedImmediate(immediateR());
345
346 return m_formatBuffer;
347 } else if (immediateS() < immediateR()) {
348 // bit field insert
349 appendInstructionName(insertOpNames());
350
351 appendRegisterName(rd(), is64Bit());
352 appendSeparator();
353 appendRegisterName(rn(), is64Bit());
354 appendSeparator();
355 appendUnsignedImmediate((is64Bit() ? 64u : 32u) - immediateR());
356 appendSeparator();
357 appendUnsignedImmediate(immediateS() + 1);
358
359 return m_formatBuffer;
360 } else {
361 // bit field extract
362 appendInstructionName(extractOpNames());
363
364 appendRegisterName(rd(), is64Bit());
365 appendSeparator();
366 appendRegisterName(rn(), is64Bit());
367 appendSeparator();
368 appendUnsignedImmediate(immediateR());
369 appendSeparator();
370 appendUnsignedImmediate(immediateS() - immediateR() + 1);
371
372 return m_formatBuffer;
373 }
374
375 appendInstructionName(opName());
376 appendRegisterName(rd(), is64Bit());
377 appendSeparator();
378 appendRegisterName(rn(), is64Bit());
379 appendSeparator();
380 appendUnsignedImmediate(immediateR());
381 appendSeparator();
382 appendUnsignedImmediate(immediateS());
383
384 return m_formatBuffer;
385}
386
387const char* A64DOpcodeCompareAndBranchImmediate::format()
388{
389 appendInstructionName(opBit() ? "cbnz" : "cbz");
390 appendRegisterName(rt(), is64Bit());
391 appendSeparator();
392 appendPCRelativeOffset(m_currentPC, static_cast<int32_t>(immediate19()));
393 return m_formatBuffer;
394}
395
396const char* A64DOpcodeConditionalBranchImmediate::format()
397{
398 bufferPrintf(" b.%-5.5s", conditionName(condition()));
399 appendPCRelativeOffset(m_currentPC, static_cast<int32_t>(immediate19()));
400 return m_formatBuffer;
401}
402
403const char* const A64DOpcodeConditionalSelect::s_opNames[4] = {
404 "csel", "csinc", "csinv", "csneg"
405};
406
407const char* A64DOpcodeConditionalSelect::format()
408{
409 if (sBit())
410 return A64DOpcode::format();
411
412 if (op2() & 0x2)
413 return A64DOpcode::format();
414
415 if (rn() == rm() && (opNum() == 1 || opNum() == 2)) {
416 if (rn() == 31) {
417 appendInstructionName((opNum() == 1) ? "cset" : "csetm");
418 appendRegisterName(rd(), is64Bit());
419 } else {
420 appendInstructionName((opNum() == 1) ? "cinc" : "cinv");
421 appendRegisterName(rd(), is64Bit());
422 appendSeparator();
423 appendZROrRegisterName(rn(), is64Bit());
424 }
425 appendSeparator();
426 appendString(conditionName(condition() ^ 0x1));
427
428 return m_formatBuffer;
429 }
430
431 appendInstructionName(opName());
432 appendRegisterName(rd(), is64Bit());
433 appendSeparator();
434 appendZROrRegisterName(rn(), is64Bit());
435 appendSeparator();
436 appendZROrRegisterName(rm(), is64Bit());
437 appendSeparator();
438 appendString(conditionName(condition()));
439
440 return m_formatBuffer;
441
442}
443
444const char* const A64DOpcodeDataProcessing2Source::s_opNames[8] = {
445 0, 0, "udiv", "sdiv", "lsl", "lsr", "asr", "ror" // We use the pseudo-op names for the shift/rotate instructions
446};
447
448const char* A64DOpcodeDataProcessing2Source::format()
449{
450 if (sBit())
451 return A64DOpcode::format();
452
453 if (!(opCode() & 0x3e))
454 return A64DOpcode::format();
455
456 if (opCode() & 0x30)
457 return A64DOpcode::format();
458
459 if ((opCode() & 0x34) == 0x4)
460 return A64DOpcode::format();
461
462 appendInstructionName(opName());
463 appendRegisterName(rd(), is64Bit());
464 appendSeparator();
465 appendRegisterName(rn(), is64Bit());
466 appendSeparator();
467 appendRegisterName(rm(), is64Bit());
468
469 return m_formatBuffer;
470}
471
472const char* const A64DOpcodeDataProcessing3Source::s_opNames[16] = {
473 "madd", "msub", "smaddl", "smsubl", "smulh", 0, 0, 0,
474 0, 0, "umaddl", "umsubl", "umulh", 0, 0, 0
475};
476
477const char* const A64DOpcodeDataProcessing3Source::s_pseudoOpNames[16] = {
478 "mul", "mneg", "smull", "smnegl", "smulh", 0, 0, 0,
479 0, 0, "umull", "umnegl", "umulh", 0, 0, 0
480};
481
482const char* A64DOpcodeDataProcessing3Source::format()
483{
484 if (op54())
485 return A64DOpcode::format();
486
487 if (opNum() > 12)
488 return A64DOpcode::format();
489
490 if (!is64Bit() && opNum() > 1)
491 return A64DOpcode::format();
492
493 if (!opName())
494 return A64DOpcode::format();
495
496 appendInstructionName(opName());
497 appendRegisterName(rd(), is64Bit());
498 appendSeparator();
499 bool srcOneAndTwoAre64Bit = is64Bit() & !(opNum() & 0x2);
500 appendRegisterName(rn(), srcOneAndTwoAre64Bit);
501 appendSeparator();
502 appendRegisterName(rm(), srcOneAndTwoAre64Bit);
503
504 if ((ra() != 31) || !(opNum() & 0x4)) {
505 appendSeparator();
506 appendRegisterName(ra(), is64Bit());
507 }
508
509 return m_formatBuffer;
510}
511
512const char* A64OpcodeExceptionGeneration::format()
513{
514 const char* opname = 0;
515 if (!op2()) {
516 switch (opc()) {
517 case 0x0: // SVC, HVC & SMC
518 switch (ll()) {
519 case 0x1:
520 opname = "svc";
521 break;
522 case 0x2:
523 opname = "hvc";
524 break;
525 case 0x3:
526 opname = "smc";
527 break;
528 }
529 break;
530 case 0x1: // BRK
531 if (!ll())
532 opname = "brk";
533 break;
534 case 0x2: // HLT
535 if (!ll())
536 opname = "hlt";
537 break;
538 case 0x5: // DPCS1-3
539 switch (ll()) {
540 case 0x1:
541 opname = "dpcs1";
542 break;
543 case 0x2:
544 opname = "dpcs2";
545 break;
546 case 0x3:
547 opname = "dpcs3";
548 break;
549 }
550 break;
551 }
552 }
553
554 if (!opname)
555 return A64DOpcode::format();
556
557 appendInstructionName(opname);
558 appendUnsignedImmediate(immediate16());
559 return m_formatBuffer;
560}
561
562const char* A64DOpcodeExtract::format()
563{
564 if (!op21() || !o0Bit())
565 return A64DOpcode::format();
566
567 if (is64Bit() != nBit())
568 return A64DOpcode::format();
569
570 if (is64Bit() && (immediateS() & 0x20))
571 return A64DOpcode::format();
572
573 const char* opName = (rn() == rm()) ? "ror" : "extr";
574
575 appendInstructionName(opName);
576 appendRegisterName(rd(), is64Bit());
577 appendSeparator();
578 appendRegisterName(rn(), is64Bit());
579 appendSeparator();
580 appendRegisterName(rm(), is64Bit());
581 appendSeparator();
582 appendUnsignedImmediate(immediateS());
583
584 return m_formatBuffer;
585}
586
587const char* A64DOpcodeFloatingPointCompare::format()
588{
589 if (mBit())
590 return A64DOpcode::format();
591
592 if (sBit())
593 return A64DOpcode::format();
594
595 if (type() & 0x2)
596 return A64DOpcode::format();
597
598 if (op())
599 return A64DOpcode::format();
600
601 if (opCode2() & 0x7)
602 return A64DOpcode::format();
603
604 appendInstructionName(opName());
605 unsigned registerSize = type() + 2;
606 appendFPRegisterName(rn(), registerSize);
607 appendSeparator();
608 if (opCode2() & 0x8)
609 bufferPrintf("#0.0");
610 else
611 appendFPRegisterName(rm(), registerSize);
612
613 return m_formatBuffer;
614}
615
616const char* const A64DOpcodeFloatingPointDataProcessing1Source::s_opNames[16] = {
617 "fmov", "fabs", "fneg", "fsqrt", "fcvt", "fcvt", 0, "fcvt",
618 "frintn", "frintp", "frintm", "frintz", "frinta", 0, "frintx", "frinti"
619};
620
621const char* A64DOpcodeFloatingPointDataProcessing1Source::format()
622{
623 if (mBit())
624 return A64DOpcode::format();
625
626 if (sBit())
627 return A64DOpcode::format();
628
629 if (opNum() > 16)
630 return A64DOpcode::format();
631
632 switch (type()) {
633 case 0:
634 if ((opNum() == 0x4) || (opNum() == 0x6) || (opNum() == 0xd))
635 return A64DOpcode::format();
636 break;
637 case 1:
638 if ((opNum() == 0x5) || (opNum() == 0x6) || (opNum() == 0xd))
639 return A64DOpcode::format();
640 break;
641 case 2:
642 return A64DOpcode::format();
643 case 3:
644 if ((opNum() < 0x4) || (opNum() > 0x5))
645 return A64DOpcode::format();
646 break;
647 }
648
649 appendInstructionName(opName());
650 if ((opNum() >= 0x4) && (opNum() <= 0x7)) {
651 unsigned srcRegisterSize = type() ^ 0x2; // 0:s, 1:d & 3:h
652 unsigned destRegisterSize = (opNum() & 0x3) ^ 0x2;
653 appendFPRegisterName(rd(), destRegisterSize);
654 appendSeparator();
655 appendFPRegisterName(rn(), srcRegisterSize);
656 } else {
657 unsigned registerSize = type() + 2;
658 appendFPRegisterName(rd(), registerSize);
659 appendSeparator();
660 appendFPRegisterName(rn(), registerSize);
661 }
662
663 return m_formatBuffer;
664}
665
666const char* const A64DOpcodeFloatingPointDataProcessing2Source::s_opNames[16] = {
667 "fmul", "fdiv", "fadd", "fsub", "fmax", "fmin", "fmaxnm", "fminnm", "fnmul"
668};
669
670const char* A64DOpcodeFloatingPointDataProcessing2Source::format()
671{
672 if (mBit())
673 return A64DOpcode::format();
674
675 if (sBit())
676 return A64DOpcode::format();
677
678 if (type() & 0x2)
679 return A64DOpcode::format();
680
681 if (opNum() > 8)
682 return A64DOpcode::format();
683
684 appendInstructionName(opName());
685 unsigned registerSize = type() + 2;
686 appendFPRegisterName(rd(), registerSize);
687 appendSeparator();
688 appendFPRegisterName(rn(), registerSize);
689 appendSeparator();
690 appendFPRegisterName(rm(), registerSize);
691
692 return m_formatBuffer;
693}
694
695const char* const A64DOpcodeFloatingFixedPointConversions::s_opNames[4] = {
696 "fcvtzs", "fcvtzu", "scvtf", "ucvtf"
697};
698
699const char* A64DOpcodeFloatingFixedPointConversions::format()
700{
701 if (sBit())
702 return A64DOpcode::format();
703
704 if (type() & 0x2)
705 return A64DOpcode::format();
706
707 if (opcode() & 0x4)
708 return A64DOpcode::format();
709
710 if (!(rmode() & 0x1) && !(opcode() & 0x6))
711 return A64DOpcode::format();
712
713 if ((rmode() & 0x1) && (opcode() & 0x6) == 0x2)
714 return A64DOpcode::format();
715
716 if (!(rmode() & 0x2) && !(opcode() & 0x6))
717 return A64DOpcode::format();
718
719 if ((rmode() & 0x2) && (opcode() & 0x6) == 0x2)
720 return A64DOpcode::format();
721
722 if (!is64Bit() && scale() >= 32)
723 return A64DOpcode::format();
724
725 appendInstructionName(opName());
726 unsigned FPRegisterSize = type() + 2;
727 bool destIsFP = !rmode();
728
729 if (destIsFP) {
730 appendFPRegisterName(rd(), FPRegisterSize);
731 appendSeparator();
732 appendRegisterName(rn(), is64Bit());
733 } else {
734 appendRegisterName(rd(), is64Bit());
735 appendSeparator();
736 appendFPRegisterName(rn(), FPRegisterSize);
737 }
738 appendSeparator();
739 appendUnsignedImmediate(64 - scale());
740
741 return m_formatBuffer;
742}
743
744const char* const A64DOpcodeFloatingPointIntegerConversions::s_opNames[32] = {
745 "fcvtns", "fcvtnu", "scvtf", "ucvtf", "fcvtas", "fcvtau", "fmov", "fmov",
746 "fcvtps", "fcvtpu", 0, 0, 0, 0, "fmov", "fmov",
747 "fcvtms", "fcvtmu", 0, 0, 0, 0, 0, 0,
748 "fcvtzs", "fcvtzu", 0, 0, 0, 0, 0, 0
749};
750
751const char* A64DOpcodeFloatingPointIntegerConversions::format()
752{
753 if (sBit())
754 return A64DOpcode::format();
755
756 if (type() == 0x3)
757 return A64DOpcode::format();
758
759 if (((rmode() & 0x1) || (rmode() & 0x2)) && (((opcode() & 0x6) == 0x2) || ((opcode() & 0x6) == 0x4)))
760 return A64DOpcode::format();
761
762 if ((type() == 0x2) && (!(opcode() & 0x4) || ((opcode() & 0x6) == 0x4)))
763 return A64DOpcode::format();
764
765 if (!type() && (rmode() & 0x1) && ((opcode() & 0x6) == 0x6))
766 return A64DOpcode::format();
767
768 if (is64Bit() && type() == 0x2 && ((opNum() & 0xe) == 0x6))
769 return A64DOpcode::format();
770
771 if (!opName())
772 return A64DOpcode::format();
773
774 if ((opNum() & 0x1e) == 0xe) {
775 // Handle fmov to/from upper half of quad separately
776 if (!is64Bit() || (type() != 0x2))
777 return A64DOpcode::format();
778
779 appendInstructionName(opName());
780 if (opcode() & 0x1) {
781 // fmov Vd.D[1], Xn
782 bufferPrintf("V%u.D[1]", rd());
783 appendSeparator();
784 appendRegisterName(rn());
785 } else {
786 // fmov Xd, Vn.D[1]
787 appendRegisterName(rd());
788 appendSeparator();
789 bufferPrintf("V%u.D[1]", rn());
790 }
791
792 return m_formatBuffer;
793 }
794
795 appendInstructionName(opName());
796 unsigned FPRegisterSize = type() + 2;
797 bool destIsFP = ((opNum() == 2) || (opNum() == 3) || (opNum() == 7));
798
799 if (destIsFP) {
800 appendFPRegisterName(rd(), FPRegisterSize);
801 appendSeparator();
802 appendRegisterName(rn(), is64Bit());
803 } else {
804 appendRegisterName(rd(), is64Bit());
805 appendSeparator();
806 appendFPRegisterName(rn(), FPRegisterSize);
807 }
808
809 return m_formatBuffer;
810}
811
812const char* const A64DOpcodeHint::s_opNames[6] = {
813 "nop", "yield", "wfe", "wfi", "sev", "sevl"
814};
815
816const char* A64DOpcodeHint::format()
817{
818 appendInstructionName(opName());
819
820 if (immediate7() > 5)
821 appendUnsignedImmediate(immediate7());
822
823 return m_formatBuffer;
824}
825
826// A zero in an entry of the table means the instruction is Unallocated
827const char* const A64DOpcodeLoadStore::s_opNames[32] = {
828 "strb", "ldrb", "ldrsb", "ldrsb", "str", "ldr", "str", "ldr",
829 "strh", "ldrh", "ldrsh", "ldrsh", "str", "ldr", 0, 0,
830 "str", "ldr", "ldrsw", 0, "str", "ldr", 0, 0,
831 "str", "ldr", 0, 0, "str", "ldr", 0, 0
832};
833
834// A zero in an entry of the table means the instruction is Unallocated
835const char* const A64DOpcodeLoadStoreImmediate::s_unprivilegedOpNames[32] = {
836 "sttrb", "ldtrb", "ldtrsb", "ldtrsb", 0, 0, 0, 0,
837 "sttrh", "ldtrh", "ldtrsh", "ldtrsh", 0, 0, 0, 0,
838 "sttr", "ldtr", "ldtrsw", 0, 0, 0, 0, 0,
839 "sttr", "ldtr", 0, 0, 0, 0, 0, 0
840};
841
842// A zero in an entry of the table means the instruction is Unallocated
843const char* const A64DOpcodeLoadStoreImmediate::s_unscaledOpNames[32] = {
844 "sturb", "ldurb", "ldursb", "ldursb", "stur", "ldur", "stur", "ldur",
845 "sturh", "ldurh", "ldursh", "ldursh", "stur", "ldur", 0, 0,
846 "stur", "ldur", "ldursw", 0, "stur", "ldur", 0, 0,
847 "stur", "ldur", "prfum", 0, "stur", "ldur", 0, 0
848};
849
850const char* A64DOpcodeLoadStoreImmediate::format()
851{
852 const char* thisOpName;
853
854 if (type() & 0x1)
855 thisOpName = opName();
856 else if (!type())
857 thisOpName = unscaledOpName();
858 else
859 thisOpName = unprivilegedOpName();
860
861 if (!thisOpName)
862 return A64DOpcode::format();
863
864 appendInstructionName(thisOpName);
865 if (vBit())
866 appendFPRegisterName(rt(), size());
867 else
868 appendRegisterName(rt(), is64BitRT());
869 appendSeparator();
870 appendCharacter('[');
871 appendSPOrRegisterName(rn());
872
873 switch (type()) {
874 case 0: // Unscaled Immediate
875 if (immediate9()) {
876 appendSeparator();
877 appendSignedImmediate(immediate9());
878 }
879 appendCharacter(']');
880 break;
881 case 1: // Immediate Post-Indexed
882 appendCharacter(']');
883 if (immediate9()) {
884 appendSeparator();
885 appendSignedImmediate(immediate9());
886 }
887 break;
888 case 2: // Unprivileged
889 if (immediate9()) {
890 appendSeparator();
891 appendSignedImmediate(immediate9());
892 }
893 appendCharacter(']');
894 break;
895 case 3: // Immediate Pre-Indexed
896 if (immediate9()) {
897 appendSeparator();
898 appendSignedImmediate(immediate9());
899 }
900 appendCharacter(']');
901 appendCharacter('!');
902 break;
903 }
904
905 return m_formatBuffer;
906}
907
908const char* A64DOpcodeLoadStoreRegisterOffset::format()
909{
910 const char* thisOpName = opName();
911
912 if (!thisOpName)
913 return A64DOpcode::format();
914
915 if (!(option() & 0x2))
916 return A64DOpcode::format();
917
918 appendInstructionName(thisOpName);
919 unsigned scale;
920 if (vBit()) {
921 appendFPRegisterName(rt(), size());
922 scale = ((opc() & 2)<<1) | size();
923 } else {
924 appendRegisterName(rt(), is64BitRT());
925 scale = size();
926 }
927 appendSeparator();
928 appendCharacter('[');
929 appendSPOrRegisterName(rn());
930 appendSeparator();
931 appendZROrRegisterName(rm(), (option() & 0x3) == 0x3);
932
933 unsigned shift = sBit() ? scale : 0;
934
935 if (option() == 0x3) {
936 if (shift) {
937 appendSeparator();
938 appendString("lsl ");
939 appendUnsignedImmediate(shift);
940 }
941 } else {
942 appendSeparator();
943 appendString(optionName());
944 if (shift)
945 appendUnsignedImmediate(shift);
946 }
947
948 appendCharacter(']');
949
950 return m_formatBuffer;
951}
952
953const char* A64DOpcodeLoadStoreRegisterPair::opName()
954{
955 if (!vBit() && lBit() && size() == 0x1)
956 return "ldpsw";
957 if (lBit())
958 return "ldp";
959 return "stp";
960}
961
962const char* A64DOpcodeLoadStoreRegisterPair::format()
963{
964 const char* thisOpName = opName();
965
966 if (size() == 0x3)
967 return A64DOpcode::format();
968
969 if ((offsetMode() < 0x1) || (offsetMode() > 0x3))
970 return A64DOpcode::format();
971
972 if ((offsetMode() == 0x1) && !vBit() && !lBit())
973 return A64DOpcode::format();
974
975 appendInstructionName(thisOpName);
976 unsigned offsetShift;
977 if (vBit()) {
978 appendFPRegisterName(rt(), size());
979 appendSeparator();
980 appendFPRegisterName(rt2(), size());
981 offsetShift = size() + 2;
982 } else {
983 appendRegisterName(rt(), is64Bit());
984 appendSeparator();
985 appendRegisterName(rt2(), is64Bit());
986 offsetShift = (size() >> 1) + 2;
987 }
988
989 appendSeparator();
990 appendCharacter('[');
991 appendSPOrRegisterName(rn());
992
993 int offset = immediate7() << offsetShift;
994
995 if (offsetMode() == 1) {
996 appendCharacter(']');
997 appendSeparator();
998 appendSignedImmediate(offset);
999 } else {
1000 appendSeparator();
1001 appendSignedImmediate(offset);
1002 appendCharacter(']');
1003 if (offsetMode() == 0x3)
1004 appendCharacter('!');
1005 }
1006
1007 return m_formatBuffer;
1008}
1009
1010const char* A64DOpcodeLoadStoreUnsignedImmediate::format()
1011{
1012 const char* thisOpName = opName();
1013
1014 if (!thisOpName)
1015 return A64DOpcode::format();
1016
1017 appendInstructionName(thisOpName);
1018 unsigned scale;
1019 if (vBit()) {
1020 appendFPRegisterName(rt(), size());
1021 scale = ((opc() & 2)<<1) | size();
1022 } else {
1023 appendRegisterName(rt(), is64BitRT());
1024 scale = size();
1025 }
1026 appendSeparator();
1027 appendCharacter('[');
1028 appendSPOrRegisterName(rn());
1029
1030 if (immediate12()) {
1031 appendSeparator();
1032 appendUnsignedImmediate(immediate12() << scale);
1033 }
1034
1035 appendCharacter(']');
1036
1037 return m_formatBuffer;
1038}
1039
1040// A zero in an entry of the table means the instruction is Unallocated
1041const char* const A64DOpcodeLogical::s_opNames[8] = {
1042 "and", "bic", "orr", "orn", "eor", "eon", "ands", "bics"
1043};
1044
1045const char* A64DOpcodeLogicalShiftedRegister::format()
1046{
1047 if (!is64Bit() && immediate6() & 0x20)
1048 return A64DOpcode::format();
1049
1050 if (isTst())
1051 appendInstructionName("tst");
1052 else {
1053 if (isMov())
1054 appendInstructionName("mov");
1055 else
1056 appendInstructionName(opName(opNumber()));
1057 appendSPOrRegisterName(rd(), is64Bit());
1058 appendSeparator();
1059 }
1060
1061 if (!isMov()) {
1062 appendRegisterName(rn(), is64Bit());
1063 appendSeparator();
1064 }
1065
1066 appendZROrRegisterName(rm(), is64Bit());
1067 if (immediate6()) {
1068 appendSeparator();
1069 appendShiftType(shift());
1070 appendUnsignedImmediate(immediate6());
1071 }
1072
1073 return m_formatBuffer;
1074}
1075
1076static unsigned highestBitSet(unsigned value)
1077{
1078 unsigned result = 0;
1079
1080 while (value >>= 1)
1081 result++;
1082
1083 return result;
1084}
1085
1086static uint64_t rotateRight(uint64_t value, unsigned width, unsigned shift)
1087{
1088 uint64_t result = value;
1089
1090 if (shift)
1091 result = (value >> (shift % width)) | (value << (width - shift));
1092
1093 return result;
1094}
1095
1096static uint64_t replicate(uint64_t value, unsigned width)
1097{
1098 uint64_t result = 0;
1099
1100 for (unsigned totalBits = 0; totalBits < 64; totalBits += width)
1101 result = (result << width) | value;
1102
1103 return result;
1104}
1105
1106const char* A64DOpcodeLogicalImmediate::format()
1107{
1108 if (!is64Bit() && nBit())
1109 return A64DOpcode::format();
1110
1111 unsigned len = highestBitSet(nBit() << 6 | (immediateS() ^ 0x3f));
1112 unsigned levels = (1 << len) - 1; // len number of 1 bits starting at LSB
1113
1114 if ((immediateS() & levels) == levels)
1115 return A64DOpcode::format();
1116
1117 unsigned r = immediateR() & levels;
1118 unsigned s = immediateS() & levels;
1119 unsigned eSize = 1 << len;
1120 uint64_t pattern = rotateRight((1ull << (s + 1)) - 1, eSize, r);
1121
1122 uint64_t immediate = replicate(pattern, eSize);
1123
1124 if (!is64Bit())
1125 immediate &= 0xffffffffull;
1126
1127 if (isTst())
1128 appendInstructionName("tst");
1129 else {
1130 if (isMov())
1131 appendInstructionName("mov");
1132 else
1133 appendInstructionName(opName(opNumber()));
1134 appendRegisterName(rd(), is64Bit());
1135 appendSeparator();
1136 }
1137 if (!isMov()) {
1138 appendRegisterName(rn(), is64Bit());
1139 appendSeparator();
1140 }
1141 appendUnsignedImmediate64(immediate);
1142
1143 return m_formatBuffer;
1144}
1145
1146const char* const A64DOpcodeMoveWide::s_opNames[4] = { "movn", "", "movz", "movk" };
1147
1148const char* A64DOpcodeMoveWide::format()
1149{
1150 if (opc() == 1)
1151 return A64DOpcode::format();
1152 if (!size() && hw() >= 2)
1153 return A64DOpcode::format();
1154
1155 appendInstructionName(opName());
1156 appendRegisterName(rd(), is64Bit());
1157 appendSeparator();
1158 appendUnsignedImmediate(immediate16());
1159 if (hw()) {
1160 appendSeparator();
1161 appendShiftAmount(hw());
1162 }
1163
1164 return m_formatBuffer;
1165}
1166
1167const char* A64DOpcodeTestAndBranchImmediate::format()
1168{
1169 appendInstructionName(opBit() ? "tbnz" : "tbz");
1170 appendRegisterName(rt());
1171 appendSeparator();
1172 appendUnsignedImmediate(bitNumber());
1173 appendSeparator();
1174 appendPCRelativeOffset(m_currentPC, static_cast<int32_t>(immediate14()));
1175 return m_formatBuffer;
1176}
1177
1178const char* A64DOpcodeUnconditionalBranchImmediate::format()
1179{
1180 appendInstructionName(op() ? "bl" : "b");
1181 appendPCRelativeOffset(m_currentPC, static_cast<int32_t>(immediate26()));
1182 return m_formatBuffer;
1183}
1184
1185const char* const A64DOpcodeUnconditionalBranchRegister::s_opNames[8] = { "br", "blr", "ret", "", "eret", "drps", "", "" };
1186
1187const char* A64DOpcodeUnconditionalBranchRegister::format()
1188{
1189 unsigned opcValue = opc();
1190 if (opcValue == 3 || opcValue > 5)
1191 return A64DOpcode::format();
1192 if (((opcValue & 0xe) == 0x4) && rn() != 0x1f)
1193 return A64DOpcode::format();
1194 appendInstructionName(opName());
1195 if (opcValue <= 2)
1196 appendRegisterName(rn());
1197 return m_formatBuffer;
1198}
1199
1200} } // namespace JSC::ARM64Disassembler
1201
1202#endif // USE(ARM64_DISASSEMBLER)
1203

source code of qtdeclarative/src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp