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 | |
37 | namespace JSC { namespace ARM64Disassembler { |
38 | |
39 | A64DOpcode::OpcodeGroup* A64DOpcode::opcodeTable[32]; |
40 | |
41 | const 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 | |
46 | const char* const A64DOpcode::s_optionName[8] = { |
47 | "uxtb" , "uxth" , "uxtw" , "uxtx" , "sxtb" , "sxth" , "sxtw" , "sxtx" |
48 | }; |
49 | |
50 | const char* const A64DOpcode::s_shiftNames[4] = { |
51 | "lsl" , "lsr" , "asl" , "ror" |
52 | }; |
53 | |
54 | const char A64DOpcode::s_FPRegisterPrefix[5] = { |
55 | 'b', 'h', 's', 'd', 'q' |
56 | }; |
57 | |
58 | struct 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 | |
68 | static 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 | |
109 | bool A64DOpcode::s_initialized = false; |
110 | |
111 | void 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 | |
137 | void 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 | |
145 | const 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 | |
160 | void 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 | |
173 | const char* A64DOpcode::format() |
174 | { |
175 | bufferPrintf(" .long %08x" , m_opcode); |
176 | return m_formatBuffer; |
177 | } |
178 | |
179 | void 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 | |
194 | void A64DOpcode::appendFPRegisterName(unsigned registerNumber, unsigned registerSize) |
195 | { |
196 | bufferPrintf("%c%u" , FPRegisterPrefix(registerSize), registerNumber); |
197 | } |
198 | |
199 | const char* const A64DOpcodeAddSubtract::s_opNames[4] = { "add" , "adds" , "sub" , "subs" }; |
200 | |
201 | const 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 | |
226 | const 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 | |
254 | const 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 | |
286 | const char* const A64DOpcodeBitfield::s_opNames[3] = { "sbfm" , "bfm" , "ubfm" }; |
287 | const char* const A64DOpcodeBitfield::s_extendPseudoOpNames[3][3] = { |
288 | { "sxtb" , "sxth" , "sxtw" }, { 0, 0, 0} , { "uxtb" , "uxth" , "uxtw" } }; |
289 | const char* const A64DOpcodeBitfield::s_insertOpNames[3] = { "sbfiz" , "bfi" , "ubfiz" }; |
290 | const char* const A64DOpcodeBitfield::s_extractOpNames[3] = { "sbfx" , "bf" , "ubfx" }; |
291 | |
292 | const 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 | |
387 | const 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 | |
396 | const 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 | |
403 | const char* const A64DOpcodeConditionalSelect::s_opNames[4] = { |
404 | "csel" , "csinc" , "csinv" , "csneg" |
405 | }; |
406 | |
407 | const 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 | |
444 | const 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 | |
448 | const 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 | |
472 | const 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 | |
477 | const 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 | |
482 | const 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 | |
512 | const 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 | |
562 | const 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 | |
587 | const 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 | |
616 | const 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 | |
621 | const 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 | |
666 | const char* const A64DOpcodeFloatingPointDataProcessing2Source::s_opNames[16] = { |
667 | "fmul" , "fdiv" , "fadd" , "fsub" , "fmax" , "fmin" , "fmaxnm" , "fminnm" , "fnmul" |
668 | }; |
669 | |
670 | const 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 | |
695 | const char* const A64DOpcodeFloatingFixedPointConversions::s_opNames[4] = { |
696 | "fcvtzs" , "fcvtzu" , "scvtf" , "ucvtf" |
697 | }; |
698 | |
699 | const 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 | |
744 | const 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 | |
751 | const 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 | |
812 | const char* const A64DOpcodeHint::s_opNames[6] = { |
813 | "nop" , "yield" , "wfe" , "wfi" , "sev" , "sevl" |
814 | }; |
815 | |
816 | const 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 |
827 | const 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 |
835 | const 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 |
843 | const 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 | |
850 | const 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 | |
908 | const 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 | |
953 | const char* A64DOpcodeLoadStoreRegisterPair::opName() |
954 | { |
955 | if (!vBit() && lBit() && size() == 0x1) |
956 | return "ldpsw" ; |
957 | if (lBit()) |
958 | return "ldp" ; |
959 | return "stp" ; |
960 | } |
961 | |
962 | const 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 | |
1010 | const 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 |
1041 | const char* const A64DOpcodeLogical::s_opNames[8] = { |
1042 | "and" , "bic" , "orr" , "orn" , "eor" , "eon" , "ands" , "bics" |
1043 | }; |
1044 | |
1045 | const 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 | |
1076 | static unsigned highestBitSet(unsigned value) |
1077 | { |
1078 | unsigned result = 0; |
1079 | |
1080 | while (value >>= 1) |
1081 | result++; |
1082 | |
1083 | return result; |
1084 | } |
1085 | |
1086 | static 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 | |
1096 | static 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 | |
1106 | const 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 | |
1146 | const char* const A64DOpcodeMoveWide::s_opNames[4] = { "movn" , "" , "movz" , "movk" }; |
1147 | |
1148 | const 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 | |
1167 | const 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 | |
1178 | const char* A64DOpcodeUnconditionalBranchImmediate::format() |
1179 | { |
1180 | appendInstructionName(op() ? "bl" : "b" ); |
1181 | appendPCRelativeOffset(m_currentPC, static_cast<int32_t>(immediate26())); |
1182 | return m_formatBuffer; |
1183 | } |
1184 | |
1185 | const char* const A64DOpcodeUnconditionalBranchRegister::s_opNames[8] = { "br" , "blr" , "ret" , "" , "eret" , "drps" , "" , "" }; |
1186 | |
1187 | const 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 | |