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