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