| 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. |
| 6 | #if defined(TARGET_ARCH_ARM) |
| 7 | |
| 8 | #include "vm/compiler/assembler/disassembler.h" |
| 9 | |
| 10 | #include "platform/assert.h" |
| 11 | #include "vm/cpu.h" |
| 12 | #include "vm/instructions.h" |
| 13 | |
| 14 | namespace dart { |
| 15 | |
| 16 | #if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER) |
| 17 | |
| 18 | class ARMDecoder : public ValueObject { |
| 19 | public: |
| 20 | ARMDecoder(char* buffer, size_t buffer_size) |
| 21 | : buffer_(buffer), buffer_size_(buffer_size), buffer_pos_(0) { |
| 22 | buffer_[buffer_pos_] = '\0'; |
| 23 | } |
| 24 | |
| 25 | ~ARMDecoder() {} |
| 26 | |
| 27 | // Writes one disassembled instruction into 'buffer' (0-terminated). |
| 28 | // Returns true if the instruction was successfully decoded, false otherwise. |
| 29 | void InstructionDecode(uword pc); |
| 30 | |
| 31 | private: |
| 32 | // Bottleneck functions to print into the out_buffer. |
| 33 | void Print(const char* str); |
| 34 | |
| 35 | // Printing of common values. |
| 36 | void PrintRegister(int reg); |
| 37 | void PrintSRegister(int reg); |
| 38 | void PrintDRegister(int reg); |
| 39 | void PrintDRegisterList(int start, int reg_count); |
| 40 | void PrintQRegister(int reg); |
| 41 | void PrintCondition(Instr* instr); |
| 42 | void PrintShiftRm(Instr* instr); |
| 43 | void PrintShiftImm(Instr* instr); |
| 44 | void PrintPU(Instr* instr); |
| 45 | |
| 46 | // Handle formatting of instructions and their options. |
| 47 | int FormatRegister(Instr* instr, const char* option); |
| 48 | int FormatSRegister(Instr* instr, const char* option); |
| 49 | int FormatDRegister(Instr* instr, const char* option); |
| 50 | int FormatQRegister(Instr* instr, const char* option); |
| 51 | int FormatOption(Instr* instr, const char* option); |
| 52 | void Format(Instr* instr, const char* format); |
| 53 | void Unknown(Instr* instr); |
| 54 | |
| 55 | // Each of these functions decodes one particular instruction type, a 3-bit |
| 56 | // field in the instruction encoding. |
| 57 | // Types 0 and 1 are combined as they are largely the same except for the way |
| 58 | // they interpret the shifter operand. |
| 59 | void DecodeType01(Instr* instr); |
| 60 | void DecodeType2(Instr* instr); |
| 61 | void DecodeType3(Instr* instr); |
| 62 | void DecodeType4(Instr* instr); |
| 63 | void DecodeType5(Instr* instr); |
| 64 | void DecodeType6(Instr* instr); |
| 65 | void DecodeType7(Instr* instr); |
| 66 | void DecodeSIMDDataProcessing(Instr* instr); |
| 67 | |
| 68 | // Convenience functions. |
| 69 | char* get_buffer() const { return buffer_; } |
| 70 | char* current_position_in_buffer() { return buffer_ + buffer_pos_; } |
| 71 | size_t remaining_size_in_buffer() { return buffer_size_ - buffer_pos_; } |
| 72 | |
| 73 | char* buffer_; // Decode instructions into this buffer. |
| 74 | size_t buffer_size_; // The size of the character buffer. |
| 75 | size_t buffer_pos_; // Current character position in buffer. |
| 76 | |
| 77 | DISALLOW_ALLOCATION(); |
| 78 | DISALLOW_COPY_AND_ASSIGN(ARMDecoder); |
| 79 | }; |
| 80 | |
| 81 | // Support for assertions in the ARMDecoder formatting functions. |
| 82 | #define STRING_STARTS_WITH(string, compare_string) \ |
| 83 | (strncmp(string, compare_string, strlen(compare_string)) == 0) |
| 84 | |
| 85 | // Append the str to the output buffer. |
| 86 | void ARMDecoder::Print(const char* str) { |
| 87 | char cur = *str++; |
| 88 | while (cur != '\0' && (buffer_pos_ < (buffer_size_ - 1))) { |
| 89 | buffer_[buffer_pos_++] = cur; |
| 90 | cur = *str++; |
| 91 | } |
| 92 | buffer_[buffer_pos_] = '\0'; |
| 93 | } |
| 94 | |
| 95 | // These condition names are defined in a way to match the native disassembler |
| 96 | // formatting. See for example the command "objdump -d <binary file>". |
| 97 | static const char* cond_names[kNumberOfConditions] = { |
| 98 | "eq" , "ne" , "cs" , "cc" , "mi" , "pl" , "vs" , "vc" , |
| 99 | "hi" , "ls" , "ge" , "lt" , "gt" , "le" , "" , "invalid" , |
| 100 | }; |
| 101 | |
| 102 | // Print the condition guarding the instruction. |
| 103 | void ARMDecoder::PrintCondition(Instr* instr) { |
| 104 | Print(cond_names[instr->ConditionField()]); |
| 105 | } |
| 106 | |
| 107 | // These register names are defined in a way to match the native disassembler |
| 108 | // formatting, except for register alias pp (r5). |
| 109 | // See for example the command "objdump -d <binary file>". |
| 110 | static const char* reg_names[kNumberOfCpuRegisters] = { |
| 111 | #if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS) |
| 112 | "r0" , "r1" , "r2" , "r3" , "r4" , "pp" , "r6" , "fp" , |
| 113 | "r8" , "r9" , "thr" , "r11" , "ip" , "sp" , "lr" , "pc" , |
| 114 | #else |
| 115 | "r0" , "r1" , "r2" , "r3" , "r4" , "pp" , "r6" , "r7" , |
| 116 | "r8" , "r9" , "thr" , "fp" , "ip" , "sp" , "lr" , "pc" , |
| 117 | #endif |
| 118 | }; |
| 119 | |
| 120 | // Print the register name according to the active name converter. |
| 121 | void ARMDecoder::PrintRegister(int reg) { |
| 122 | ASSERT(0 <= reg); |
| 123 | ASSERT(reg < kNumberOfCpuRegisters); |
| 124 | Print(reg_names[reg]); |
| 125 | } |
| 126 | |
| 127 | void ARMDecoder::PrintSRegister(int reg) { |
| 128 | ASSERT(0 <= reg); |
| 129 | ASSERT(reg < kNumberOfSRegisters); |
| 130 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 131 | remaining_size_in_buffer(), "s%d" , reg); |
| 132 | } |
| 133 | |
| 134 | void ARMDecoder::PrintDRegister(int reg) { |
| 135 | ASSERT(0 <= reg); |
| 136 | ASSERT(reg < kNumberOfDRegisters); |
| 137 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 138 | remaining_size_in_buffer(), "d%d" , reg); |
| 139 | } |
| 140 | |
| 141 | void ARMDecoder::PrintQRegister(int reg) { |
| 142 | ASSERT(0 <= reg); |
| 143 | ASSERT(reg < kNumberOfQRegisters); |
| 144 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 145 | remaining_size_in_buffer(), "q%d" , reg); |
| 146 | } |
| 147 | |
| 148 | // These shift names are defined in a way to match the native disassembler |
| 149 | // formatting. See for example the command "objdump -d <binary file>". |
| 150 | static const char* shift_names[kMaxShift] = {"lsl" , "lsr" , "asr" , "ror" }; |
| 151 | |
| 152 | // Print the register shift operands for the instruction. Generally used for |
| 153 | // data processing instructions. |
| 154 | void ARMDecoder::PrintShiftRm(Instr* instr) { |
| 155 | Shift shift = instr->ShiftField(); |
| 156 | int shift_amount = instr->ShiftAmountField(); |
| 157 | int rm = instr->RmField(); |
| 158 | |
| 159 | PrintRegister(rm); |
| 160 | |
| 161 | if ((instr->RegShiftField() == 0) && (shift == LSL) && (shift_amount == 0)) { |
| 162 | // Special case for using rm only. |
| 163 | return; |
| 164 | } |
| 165 | if (instr->RegShiftField() == 0) { |
| 166 | // by immediate |
| 167 | if ((shift == ROR) && (shift_amount == 0)) { |
| 168 | Print(", RRX" ); |
| 169 | return; |
| 170 | } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) { |
| 171 | shift_amount = 32; |
| 172 | } |
| 173 | buffer_pos_ += |
| 174 | Utils::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(), |
| 175 | ", %s #%d" , shift_names[shift], shift_amount); |
| 176 | } else { |
| 177 | // by register |
| 178 | int rs = instr->RsField(); |
| 179 | buffer_pos_ += |
| 180 | Utils::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(), |
| 181 | ", %s " , shift_names[shift]); |
| 182 | PrintRegister(rs); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | // Print the immediate operand for the instruction. Generally used for data |
| 187 | // processing instructions. |
| 188 | void ARMDecoder::PrintShiftImm(Instr* instr) { |
| 189 | uint8_t rotate = instr->RotateField() * 2; |
| 190 | int32_t immed8 = instr->Immed8Field(); |
| 191 | int32_t imm = Utils::RotateRight(immed8, rotate); |
| 192 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 193 | remaining_size_in_buffer(), "#%d" , imm); |
| 194 | } |
| 195 | |
| 196 | // Print PU formatting to reduce complexity of FormatOption. |
| 197 | void ARMDecoder::PrintPU(Instr* instr) { |
| 198 | switch (instr->PUField()) { |
| 199 | case 0: { |
| 200 | Print("da" ); |
| 201 | break; |
| 202 | } |
| 203 | case 1: { |
| 204 | Print("ia" ); |
| 205 | break; |
| 206 | } |
| 207 | case 2: { |
| 208 | Print("db" ); |
| 209 | break; |
| 210 | } |
| 211 | case 3: { |
| 212 | Print("ib" ); |
| 213 | break; |
| 214 | } |
| 215 | default: { |
| 216 | UNREACHABLE(); |
| 217 | break; |
| 218 | } |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | // Handle all register based formatting in these functions to reduce the |
| 223 | // complexity of FormatOption. |
| 224 | int ARMDecoder::FormatRegister(Instr* instr, const char* format) { |
| 225 | ASSERT(format[0] == 'r'); |
| 226 | if (format[1] == 'n') { // 'rn: Rn register |
| 227 | int reg = instr->RnField(); |
| 228 | PrintRegister(reg); |
| 229 | return 2; |
| 230 | } else if (format[1] == 'd') { // 'rd: Rd register |
| 231 | int reg = instr->RdField(); |
| 232 | PrintRegister(reg); |
| 233 | if (format[2] == '2') { // 'rd2: possibly Rd, Rd+1 register pair |
| 234 | if (instr->HasSign() && !instr->HasL()) { |
| 235 | if ((reg % 2) != 0) { |
| 236 | Print(" *** unknown (odd register pair) ***" ); |
| 237 | } else { |
| 238 | Print(", " ); |
| 239 | PrintRegister(reg + 1); |
| 240 | } |
| 241 | } |
| 242 | return 3; |
| 243 | } |
| 244 | return 2; |
| 245 | } else if (format[1] == 's') { // 'rs: Rs register |
| 246 | int reg = instr->RsField(); |
| 247 | PrintRegister(reg); |
| 248 | return 2; |
| 249 | } else if (format[1] == 'm') { // 'rm: Rm register |
| 250 | int reg = instr->RmField(); |
| 251 | PrintRegister(reg); |
| 252 | return 2; |
| 253 | } else if (format[1] == 'l') { |
| 254 | // 'rlist: register list for load and store multiple instructions |
| 255 | ASSERT(STRING_STARTS_WITH(format, "rlist" )); |
| 256 | int rlist = instr->RlistField(); |
| 257 | int reg = 0; |
| 258 | Print("{" ); |
| 259 | // Print register list in ascending order, by scanning the bit mask. |
| 260 | while (rlist != 0) { |
| 261 | if ((rlist & 1) != 0) { |
| 262 | PrintRegister(reg); |
| 263 | if ((rlist >> 1) != 0) { |
| 264 | Print(", " ); |
| 265 | } |
| 266 | } |
| 267 | reg++; |
| 268 | rlist >>= 1; |
| 269 | } |
| 270 | Print("}" ); |
| 271 | return 5; |
| 272 | } |
| 273 | UNREACHABLE(); |
| 274 | return -1; |
| 275 | } |
| 276 | |
| 277 | int ARMDecoder::FormatSRegister(Instr* instr, const char* format) { |
| 278 | ASSERT(format[0] == 's'); |
| 279 | if (format[1] == 'n') { // 'sn: Sn register |
| 280 | int reg = instr->SnField(); |
| 281 | PrintSRegister(reg); |
| 282 | return 2; |
| 283 | } else if (format[1] == 'd') { // 'sd: Sd register |
| 284 | int reg = instr->SdField(); |
| 285 | PrintSRegister(reg); |
| 286 | return 2; |
| 287 | } else if (format[1] == 'm') { |
| 288 | int reg = instr->SmField(); |
| 289 | if (format[2] == '1') { // 'sm1: S[m+1] register |
| 290 | reg++; |
| 291 | ASSERT(reg < kNumberOfSRegisters); |
| 292 | PrintSRegister(reg); |
| 293 | return 3; |
| 294 | } else { // 'sm: Sm register |
| 295 | PrintSRegister(reg); |
| 296 | return 2; |
| 297 | } |
| 298 | } else if (format[1] == 'l') { |
| 299 | ASSERT(STRING_STARTS_WITH(format, "slist" )); |
| 300 | int reg_count = instr->Bits(0, 8); |
| 301 | int start = instr->Bit(22) | (instr->Bits(12, 4) << 1); |
| 302 | Print("{" ); |
| 303 | for (int i = start; i < start + reg_count; i++) { |
| 304 | PrintSRegister(i); |
| 305 | if (i != start + reg_count - 1) { |
| 306 | Print(", " ); |
| 307 | } |
| 308 | } |
| 309 | Print("}" ); |
| 310 | return 5; |
| 311 | } |
| 312 | UNREACHABLE(); |
| 313 | return -1; |
| 314 | } |
| 315 | |
| 316 | void ARMDecoder::PrintDRegisterList(int start, int reg_count) { |
| 317 | Print("{" ); |
| 318 | for (int i = start; i < start + reg_count; i++) { |
| 319 | PrintDRegister(i); |
| 320 | if (i != start + reg_count - 1) { |
| 321 | Print(", " ); |
| 322 | } |
| 323 | } |
| 324 | Print("}" ); |
| 325 | } |
| 326 | |
| 327 | int ARMDecoder::FormatDRegister(Instr* instr, const char* format) { |
| 328 | ASSERT(format[0] == 'd'); |
| 329 | if (format[1] == 'n') { // 'dn: Dn register |
| 330 | int reg = instr->DnField(); |
| 331 | PrintDRegister(reg); |
| 332 | return 2; |
| 333 | } else if (format[1] == 'd') { // 'dd: Dd register |
| 334 | int reg = instr->DdField(); |
| 335 | PrintDRegister(reg); |
| 336 | return 2; |
| 337 | } else if (format[1] == 'm') { // 'dm: Dm register |
| 338 | int reg = instr->DmField(); |
| 339 | PrintDRegister(reg); |
| 340 | return 2; |
| 341 | } else if (format[1] == 'l') { |
| 342 | ASSERT(STRING_STARTS_WITH(format, "dlist" )); |
| 343 | int reg_count = instr->Bits(0, 8) >> 1; |
| 344 | int start = (instr->Bit(22) << 4) | instr->Bits(12, 4); |
| 345 | PrintDRegisterList(start, reg_count); |
| 346 | return 5; |
| 347 | } else if (format[1] == 't') { |
| 348 | ASSERT(STRING_STARTS_WITH(format, "dtbllist" )); |
| 349 | int reg_count = instr->Bits(8, 2) + 1; |
| 350 | int start = (instr->Bit(7) << 4) | instr->Bits(16, 4); |
| 351 | PrintDRegisterList(start, reg_count); |
| 352 | return 8; |
| 353 | } |
| 354 | UNREACHABLE(); |
| 355 | return -1; |
| 356 | } |
| 357 | |
| 358 | int ARMDecoder::FormatQRegister(Instr* instr, const char* format) { |
| 359 | ASSERT(format[0] == 'q'); |
| 360 | if (format[1] == 'n') { // 'qn: Qn register |
| 361 | int reg = instr->QnField(); |
| 362 | PrintQRegister(reg); |
| 363 | return 2; |
| 364 | } else if (format[1] == 'd') { // 'qd: Qd register |
| 365 | int reg = instr->QdField(); |
| 366 | PrintQRegister(reg); |
| 367 | return 2; |
| 368 | } else if (format[1] == 'm') { // 'qm: Qm register |
| 369 | int reg = instr->QmField(); |
| 370 | PrintQRegister(reg); |
| 371 | return 2; |
| 372 | } |
| 373 | UNREACHABLE(); |
| 374 | return -1; |
| 375 | } |
| 376 | |
| 377 | // FormatOption takes a formatting string and interprets it based on |
| 378 | // the current instructions. The format string points to the first |
| 379 | // character of the option string (the option escape has already been |
| 380 | // consumed by the caller.) FormatOption returns the number of |
| 381 | // characters that were consumed from the formatting string. |
| 382 | int ARMDecoder::FormatOption(Instr* instr, const char* format) { |
| 383 | switch (format[0]) { |
| 384 | case 'a': { // 'a: accumulate multiplies |
| 385 | if (instr->Bit(21) == 0) { |
| 386 | Print("ul" ); |
| 387 | } else { |
| 388 | Print("la" ); |
| 389 | } |
| 390 | return 1; |
| 391 | } |
| 392 | case 'b': { // 'b: byte loads or stores |
| 393 | if (instr->HasB()) { |
| 394 | Print("b" ); |
| 395 | } |
| 396 | return 1; |
| 397 | } |
| 398 | case 'c': { // 'cond: conditional execution |
| 399 | ASSERT(STRING_STARTS_WITH(format, "cond" )); |
| 400 | PrintCondition(instr); |
| 401 | return 4; |
| 402 | } |
| 403 | case 'd': { |
| 404 | if (format[1] == 'e') { // 'dest: branch destination |
| 405 | ASSERT(STRING_STARTS_WITH(format, "dest" )); |
| 406 | const int32_t off = |
| 407 | (static_cast<uint32_t>(instr->SImmed24Field()) << 2) + 8; |
| 408 | if (FLAG_disassemble_relative) { |
| 409 | buffer_pos_ += |
| 410 | Utils::SNPrint(current_position_in_buffer(), |
| 411 | remaining_size_in_buffer(), "%+" Pd32 "" , off); |
| 412 | } else { |
| 413 | uword destination = reinterpret_cast<uword>(instr) + off; |
| 414 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 415 | remaining_size_in_buffer(), "%#" Px "" , |
| 416 | destination); |
| 417 | } |
| 418 | return 4; |
| 419 | } else { |
| 420 | return FormatDRegister(instr, format); |
| 421 | } |
| 422 | } |
| 423 | case 'q': { |
| 424 | return FormatQRegister(instr, format); |
| 425 | } |
| 426 | case 'i': { // 'imm12_4, imm4_12, immf, or immd |
| 427 | uint16_t immed16; |
| 428 | if (format[3] == 'f') { |
| 429 | ASSERT(STRING_STARTS_WITH(format, "immf" )); |
| 430 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 431 | remaining_size_in_buffer(), "%f" , |
| 432 | instr->ImmFloatField()); |
| 433 | return 4; |
| 434 | } else if (format[3] == 'd') { |
| 435 | ASSERT(STRING_STARTS_WITH(format, "immd" )); |
| 436 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 437 | remaining_size_in_buffer(), "%g" , |
| 438 | instr->ImmDoubleField()); |
| 439 | return 4; |
| 440 | } else if (format[3] == '1') { |
| 441 | ASSERT(STRING_STARTS_WITH(format, "imm12_4" )); |
| 442 | immed16 = instr->BkptField(); |
| 443 | } else { |
| 444 | ASSERT(format[3] == '4'); |
| 445 | if (format[5] == 'v') { |
| 446 | ASSERT(STRING_STARTS_WITH(format, "imm4_vdup" )); |
| 447 | int32_t idx = -1; |
| 448 | int32_t imm4 = instr->Bits(16, 4); |
| 449 | if ((imm4 & 1) != 0) |
| 450 | idx = imm4 >> 1; |
| 451 | else if ((imm4 & 2) != 0) |
| 452 | idx = imm4 >> 2; |
| 453 | else if ((imm4 & 4) != 0) |
| 454 | idx = imm4 >> 3; |
| 455 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 456 | remaining_size_in_buffer(), "%d" , idx); |
| 457 | return 9; |
| 458 | } else { |
| 459 | ASSERT(STRING_STARTS_WITH(format, "imm4_12" )); |
| 460 | immed16 = instr->MovwField(); |
| 461 | } |
| 462 | } |
| 463 | buffer_pos_ += |
| 464 | Utils::SNPrint(current_position_in_buffer(), |
| 465 | remaining_size_in_buffer(), "0x%x" , immed16); |
| 466 | return 7; |
| 467 | } |
| 468 | case 'l': { // 'l: branch and link |
| 469 | if (instr->HasLink()) { |
| 470 | Print("l" ); |
| 471 | } |
| 472 | return 1; |
| 473 | } |
| 474 | case 'm': { // 'memop: load/store instructions |
| 475 | ASSERT(STRING_STARTS_WITH(format, "memop" )); |
| 476 | if (instr->HasL() || |
| 477 | // Extra load/store instructions. |
| 478 | ((instr->TypeField() == 0) && instr->HasSign() && !instr->HasH())) { |
| 479 | Print("ldr" ); |
| 480 | } else { |
| 481 | Print("str" ); |
| 482 | } |
| 483 | return 5; |
| 484 | } |
| 485 | case 'o': { |
| 486 | if (format[3] == '1') { |
| 487 | if (format[4] == '0') { |
| 488 | // 'off10: 10-bit offset for VFP load and store instructions |
| 489 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 490 | remaining_size_in_buffer(), "%d" , |
| 491 | instr->Bits(0, 8) << 2); |
| 492 | } else { |
| 493 | // 'off12: 12-bit offset for load and store instructions. |
| 494 | ASSERT(STRING_STARTS_WITH(format, "off12" )); |
| 495 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 496 | remaining_size_in_buffer(), "%d" , |
| 497 | instr->Offset12Field()); |
| 498 | } |
| 499 | return 5; |
| 500 | } |
| 501 | // 'off8: 8-bit offset for extra load and store instructions. |
| 502 | ASSERT(STRING_STARTS_WITH(format, "off8" )); |
| 503 | int offs8 = (instr->ImmedHField() << 4) | instr->ImmedLField(); |
| 504 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 505 | remaining_size_in_buffer(), "%d" , offs8); |
| 506 | return 4; |
| 507 | } |
| 508 | case 'p': { // 'pu: P and U bits for load and store instructions. |
| 509 | ASSERT(STRING_STARTS_WITH(format, "pu" )); |
| 510 | PrintPU(instr); |
| 511 | return 2; |
| 512 | } |
| 513 | case 'r': { |
| 514 | return FormatRegister(instr, format); |
| 515 | } |
| 516 | case 's': { |
| 517 | if (format[1] == 'h') { // 'shift_op or 'shift_rm |
| 518 | if (format[6] == 'o') { // 'shift_op |
| 519 | ASSERT(STRING_STARTS_WITH(format, "shift_op" )); |
| 520 | if (instr->TypeField() == 0) { |
| 521 | PrintShiftRm(instr); |
| 522 | } else { |
| 523 | ASSERT(instr->TypeField() == 1); |
| 524 | PrintShiftImm(instr); |
| 525 | } |
| 526 | return 8; |
| 527 | } else { // 'shift_rm |
| 528 | ASSERT(STRING_STARTS_WITH(format, "shift_rm" )); |
| 529 | PrintShiftRm(instr); |
| 530 | return 8; |
| 531 | } |
| 532 | } else if (format[1] == 'v') { // 'svc |
| 533 | ASSERT(STRING_STARTS_WITH(format, "svc" )); |
| 534 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 535 | remaining_size_in_buffer(), "0x%x" , |
| 536 | instr->SvcField()); |
| 537 | return 3; |
| 538 | } else if (format[1] == 'z') { |
| 539 | // 'sz: Size field of SIMD instructions. |
| 540 | int sz = instr->Bits(20, 2); |
| 541 | char const* sz_str; |
| 542 | switch (sz) { |
| 543 | case 0: |
| 544 | sz_str = "b" ; |
| 545 | break; |
| 546 | case 1: |
| 547 | sz_str = "h" ; |
| 548 | break; |
| 549 | case 2: |
| 550 | sz_str = "w" ; |
| 551 | break; |
| 552 | case 3: |
| 553 | sz_str = "l" ; |
| 554 | break; |
| 555 | default: |
| 556 | sz_str = "?" ; |
| 557 | break; |
| 558 | } |
| 559 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 560 | remaining_size_in_buffer(), "%s" , sz_str); |
| 561 | return 2; |
| 562 | } else if (format[1] == ' ') { |
| 563 | // 's: S field of data processing instructions. |
| 564 | if (instr->HasS()) { |
| 565 | Print("s" ); |
| 566 | } |
| 567 | return 1; |
| 568 | } else { |
| 569 | return FormatSRegister(instr, format); |
| 570 | } |
| 571 | } |
| 572 | case 't': { // 'target: target of branch instructions. |
| 573 | ASSERT(STRING_STARTS_WITH(format, "target" )); |
| 574 | int32_t off = (static_cast<uint32_t>(instr->SImmed24Field()) << 2) + 8; |
| 575 | buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), |
| 576 | remaining_size_in_buffer(), "%+d" , off); |
| 577 | return 6; |
| 578 | } |
| 579 | case 'u': { // 'u: signed or unsigned multiplies. |
| 580 | if (instr->Bit(22) == 0) { |
| 581 | Print("u" ); |
| 582 | } else { |
| 583 | Print("s" ); |
| 584 | } |
| 585 | return 1; |
| 586 | } |
| 587 | case 'w': { // 'w: W field of load and store instructions. |
| 588 | if (instr->HasW()) { |
| 589 | Print("!" ); |
| 590 | } |
| 591 | return 1; |
| 592 | } |
| 593 | case 'x': { // 'x: type of extra load/store instructions. |
| 594 | if (!instr->HasSign()) { |
| 595 | Print("h" ); |
| 596 | } else if (instr->HasL()) { |
| 597 | if (instr->HasH()) { |
| 598 | Print("sh" ); |
| 599 | } else { |
| 600 | Print("sb" ); |
| 601 | } |
| 602 | } else { |
| 603 | Print("d" ); |
| 604 | } |
| 605 | return 1; |
| 606 | } |
| 607 | default: { |
| 608 | UNREACHABLE(); |
| 609 | break; |
| 610 | } |
| 611 | } |
| 612 | UNREACHABLE(); |
| 613 | return -1; |
| 614 | } |
| 615 | |
| 616 | // Format takes a formatting string for a whole instruction and prints it into |
| 617 | // the output buffer. All escaped options are handed to FormatOption to be |
| 618 | // parsed further. |
| 619 | void ARMDecoder::Format(Instr* instr, const char* format) { |
| 620 | char cur = *format++; |
| 621 | while ((cur != 0) && (buffer_pos_ < (buffer_size_ - 1))) { |
| 622 | if (cur == '\'') { // Single quote is used as the formatting escape. |
| 623 | format += FormatOption(instr, format); |
| 624 | } else { |
| 625 | buffer_[buffer_pos_++] = cur; |
| 626 | } |
| 627 | cur = *format++; |
| 628 | } |
| 629 | buffer_[buffer_pos_] = '\0'; |
| 630 | } |
| 631 | |
| 632 | // For currently unimplemented decodings the disassembler calls Unknown(instr) |
| 633 | // which will just print "unknown" of the instruction bits. |
| 634 | void ARMDecoder::Unknown(Instr* instr) { |
| 635 | Format(instr, "unknown" ); |
| 636 | } |
| 637 | |
| 638 | void ARMDecoder::DecodeType01(Instr* instr) { |
| 639 | if (!instr->IsDataProcessing()) { |
| 640 | // miscellaneous, multiply, sync primitives, extra loads and stores. |
| 641 | if (instr->IsMiscellaneous()) { |
| 642 | switch (instr->Bits(4, 3)) { |
| 643 | case 1: { |
| 644 | if (instr->Bits(21, 2) == 0x3) { |
| 645 | Format(instr, "clz'cond 'rd, 'rm" ); |
| 646 | } else if (instr->Bits(21, 2) == 0x1) { |
| 647 | Format(instr, "bx'cond 'rm" ); |
| 648 | } else { |
| 649 | Unknown(instr); |
| 650 | } |
| 651 | break; |
| 652 | } |
| 653 | case 3: { |
| 654 | if (instr->Bits(21, 2) == 0x1) { |
| 655 | Format(instr, "blx'cond 'rm" ); |
| 656 | } else { |
| 657 | // Could be inlined constant. |
| 658 | Unknown(instr); |
| 659 | } |
| 660 | break; |
| 661 | } |
| 662 | case 7: { |
| 663 | if ((instr->Bits(21, 2) == 0x1) && (instr->ConditionField() == AL)) { |
| 664 | Format(instr, "bkpt #'imm12_4" ); |
| 665 | } else { |
| 666 | // Format(instr, "smc'cond"); |
| 667 | Unknown(instr); // Not used. |
| 668 | } |
| 669 | break; |
| 670 | } |
| 671 | default: { |
| 672 | Unknown(instr); // Not used. |
| 673 | break; |
| 674 | } |
| 675 | } |
| 676 | } else if (instr->IsMultiplyOrSyncPrimitive()) { |
| 677 | if (instr->Bit(24) == 0) { |
| 678 | // multiply instructions |
| 679 | switch (instr->Bits(21, 3)) { |
| 680 | case 0: { |
| 681 | // Assembler registers rd, rn, rm are encoded as rn, rm, rs. |
| 682 | Format(instr, "mul'cond's 'rn, 'rm, 'rs" ); |
| 683 | break; |
| 684 | } |
| 685 | case 1: { |
| 686 | // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. |
| 687 | Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd" ); |
| 688 | break; |
| 689 | } |
| 690 | case 2: { |
| 691 | // Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. |
| 692 | Format(instr, "umaal'cond's 'rd, 'rn, 'rm, 'rs" ); |
| 693 | break; |
| 694 | } |
| 695 | case 3: { |
| 696 | // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. |
| 697 | Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd" ); |
| 698 | break; |
| 699 | } |
| 700 | case 4: { |
| 701 | // Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. |
| 702 | Format(instr, "umull'cond's 'rd, 'rn, 'rm, 'rs" ); |
| 703 | break; |
| 704 | } |
| 705 | case 5: { |
| 706 | // Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. |
| 707 | Format(instr, "umlal'cond's 'rd, 'rn, 'rm, 'rs" ); |
| 708 | break; |
| 709 | } |
| 710 | case 6: { |
| 711 | // Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. |
| 712 | Format(instr, "smull'cond's 'rd, 'rn, 'rm, 'rs" ); |
| 713 | break; |
| 714 | } |
| 715 | default: { |
| 716 | Unknown(instr); // Not used. |
| 717 | break; |
| 718 | } |
| 719 | } |
| 720 | } else { |
| 721 | // synchronization primitives |
| 722 | switch (instr->Bits(20, 4)) { |
| 723 | case 8: { |
| 724 | Format(instr, "strex'cond 'rd, 'rm, ['rn]" ); |
| 725 | break; |
| 726 | } |
| 727 | case 9: { |
| 728 | Format(instr, "ldrex'cond 'rd, ['rn]" ); |
| 729 | break; |
| 730 | } |
| 731 | default: { |
| 732 | Unknown(instr); // Not used. |
| 733 | break; |
| 734 | } |
| 735 | } |
| 736 | } |
| 737 | } else if (instr->Bit(25) == 1) { |
| 738 | // 16-bit immediate loads, msr (immediate), and hints |
| 739 | switch (instr->Bits(20, 5)) { |
| 740 | case 16: { |
| 741 | Format(instr, "movw'cond 'rd, #'imm4_12" ); |
| 742 | break; |
| 743 | } |
| 744 | case 18: { |
| 745 | if ((instr->Bits(16, 4) == 0) && (instr->Bits(0, 8) == 0)) { |
| 746 | Format(instr, "nop'cond" ); |
| 747 | } else { |
| 748 | Unknown(instr); // Not used. |
| 749 | } |
| 750 | break; |
| 751 | } |
| 752 | case 20: { |
| 753 | Format(instr, "movt'cond 'rd, #'imm4_12" ); |
| 754 | break; |
| 755 | } |
| 756 | default: { |
| 757 | Unknown(instr); // Not used. |
| 758 | break; |
| 759 | } |
| 760 | } |
| 761 | } else { |
| 762 | // extra load/store instructions |
| 763 | switch (instr->PUField()) { |
| 764 | case 0: { |
| 765 | if (instr->Bit(22) == 0) { |
| 766 | Format(instr, "'memop'cond'x 'rd2, ['rn], -'rm" ); |
| 767 | } else { |
| 768 | Format(instr, "'memop'cond'x 'rd2, ['rn], #-'off8" ); |
| 769 | } |
| 770 | break; |
| 771 | } |
| 772 | case 1: { |
| 773 | if (instr->Bit(22) == 0) { |
| 774 | Format(instr, "'memop'cond'x 'rd2, ['rn], +'rm" ); |
| 775 | } else { |
| 776 | Format(instr, "'memop'cond'x 'rd2, ['rn], #+'off8" ); |
| 777 | } |
| 778 | break; |
| 779 | } |
| 780 | case 2: { |
| 781 | if (instr->Bit(22) == 0) { |
| 782 | Format(instr, "'memop'cond'x 'rd2, ['rn, -'rm]'w" ); |
| 783 | } else { |
| 784 | Format(instr, "'memop'cond'x 'rd2, ['rn, #-'off8]'w" ); |
| 785 | } |
| 786 | break; |
| 787 | } |
| 788 | case 3: { |
| 789 | if (instr->Bit(22) == 0) { |
| 790 | Format(instr, "'memop'cond'x 'rd2, ['rn, +'rm]'w" ); |
| 791 | } else { |
| 792 | Format(instr, "'memop'cond'x 'rd2, ['rn, #+'off8]'w" ); |
| 793 | } |
| 794 | break; |
| 795 | } |
| 796 | default: { |
| 797 | // The PU field is a 2-bit field. |
| 798 | UNREACHABLE(); |
| 799 | break; |
| 800 | } |
| 801 | } |
| 802 | } |
| 803 | } else { |
| 804 | switch (instr->OpcodeField()) { |
| 805 | case AND: { |
| 806 | Format(instr, "and'cond's 'rd, 'rn, 'shift_op" ); |
| 807 | break; |
| 808 | } |
| 809 | case EOR: { |
| 810 | Format(instr, "eor'cond's 'rd, 'rn, 'shift_op" ); |
| 811 | break; |
| 812 | } |
| 813 | case SUB: { |
| 814 | Format(instr, "sub'cond's 'rd, 'rn, 'shift_op" ); |
| 815 | break; |
| 816 | } |
| 817 | case RSB: { |
| 818 | Format(instr, "rsb'cond's 'rd, 'rn, 'shift_op" ); |
| 819 | break; |
| 820 | } |
| 821 | case ADD: { |
| 822 | Format(instr, "add'cond's 'rd, 'rn, 'shift_op" ); |
| 823 | break; |
| 824 | } |
| 825 | case ADC: { |
| 826 | Format(instr, "adc'cond's 'rd, 'rn, 'shift_op" ); |
| 827 | break; |
| 828 | } |
| 829 | case SBC: { |
| 830 | Format(instr, "sbc'cond's 'rd, 'rn, 'shift_op" ); |
| 831 | break; |
| 832 | } |
| 833 | case RSC: { |
| 834 | Format(instr, "rsc'cond's 'rd, 'rn, 'shift_op" ); |
| 835 | break; |
| 836 | } |
| 837 | case TST: { |
| 838 | if (instr->HasS()) { |
| 839 | Format(instr, "tst'cond 'rn, 'shift_op" ); |
| 840 | } else { |
| 841 | Unknown(instr); // Not used. |
| 842 | } |
| 843 | break; |
| 844 | } |
| 845 | case TEQ: { |
| 846 | if (instr->HasS()) { |
| 847 | Format(instr, "teq'cond 'rn, 'shift_op" ); |
| 848 | } else { |
| 849 | Unknown(instr); // Not used. |
| 850 | } |
| 851 | break; |
| 852 | } |
| 853 | case CMP: { |
| 854 | if (instr->HasS()) { |
| 855 | Format(instr, "cmp'cond 'rn, 'shift_op" ); |
| 856 | } else { |
| 857 | Unknown(instr); // Not used. |
| 858 | } |
| 859 | break; |
| 860 | } |
| 861 | case CMN: { |
| 862 | if (instr->HasS()) { |
| 863 | Format(instr, "cmn'cond 'rn, 'shift_op" ); |
| 864 | } else { |
| 865 | Unknown(instr); // Not used. |
| 866 | } |
| 867 | break; |
| 868 | } |
| 869 | case ORR: { |
| 870 | Format(instr, "orr'cond's 'rd, 'rn, 'shift_op" ); |
| 871 | break; |
| 872 | } |
| 873 | case MOV: { |
| 874 | Format(instr, "mov'cond's 'rd, 'shift_op" ); |
| 875 | break; |
| 876 | } |
| 877 | case BIC: { |
| 878 | Format(instr, "bic'cond's 'rd, 'rn, 'shift_op" ); |
| 879 | break; |
| 880 | } |
| 881 | case MVN: { |
| 882 | Format(instr, "mvn'cond's 'rd, 'shift_op" ); |
| 883 | break; |
| 884 | } |
| 885 | default: { |
| 886 | // The Opcode field is a 4-bit field. |
| 887 | UNREACHABLE(); |
| 888 | break; |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | } |
| 893 | |
| 894 | void ARMDecoder::DecodeType2(Instr* instr) { |
| 895 | switch (instr->PUField()) { |
| 896 | case 0: { |
| 897 | if (instr->HasW()) { |
| 898 | Unknown(instr); // Not used. |
| 899 | } else { |
| 900 | Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12" ); |
| 901 | } |
| 902 | break; |
| 903 | } |
| 904 | case 1: { |
| 905 | if (instr->HasW()) { |
| 906 | Unknown(instr); // Not used. |
| 907 | } else { |
| 908 | Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12" ); |
| 909 | } |
| 910 | break; |
| 911 | } |
| 912 | case 2: { |
| 913 | Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w" ); |
| 914 | break; |
| 915 | } |
| 916 | case 3: { |
| 917 | Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w" ); |
| 918 | break; |
| 919 | } |
| 920 | default: { |
| 921 | // The PU field is a 2-bit field. |
| 922 | UNREACHABLE(); |
| 923 | break; |
| 924 | } |
| 925 | } |
| 926 | } |
| 927 | |
| 928 | void ARMDecoder::DecodeType3(Instr* instr) { |
| 929 | if (instr->IsDivision()) { |
| 930 | if (!TargetCPUFeatures::integer_division_supported()) { |
| 931 | Unknown(instr); |
| 932 | return; |
| 933 | } |
| 934 | if (instr->Bit(21)) { |
| 935 | Format(instr, "udiv'cond 'rn, 'rs, 'rm" ); |
| 936 | } else { |
| 937 | Format(instr, "sdiv'cond 'rn, 'rs, 'rm" ); |
| 938 | } |
| 939 | return; |
| 940 | } |
| 941 | switch (instr->PUField()) { |
| 942 | case 0: { |
| 943 | if (instr->HasW()) { |
| 944 | Unknown(instr); |
| 945 | } else { |
| 946 | Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm" ); |
| 947 | } |
| 948 | break; |
| 949 | } |
| 950 | case 1: { |
| 951 | if (instr->HasW()) { |
| 952 | Unknown(instr); |
| 953 | } else { |
| 954 | Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm" ); |
| 955 | } |
| 956 | break; |
| 957 | } |
| 958 | case 2: { |
| 959 | Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w" ); |
| 960 | break; |
| 961 | } |
| 962 | case 3: { |
| 963 | Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w" ); |
| 964 | break; |
| 965 | } |
| 966 | default: { |
| 967 | // The PU field is a 2-bit field. |
| 968 | UNREACHABLE(); |
| 969 | break; |
| 970 | } |
| 971 | } |
| 972 | } |
| 973 | |
| 974 | void ARMDecoder::DecodeType4(Instr* instr) { |
| 975 | if (instr->Bit(22) == 1) { |
| 976 | Unknown(instr); // Privileged mode currently not supported. |
| 977 | } else if (instr->HasL()) { |
| 978 | Format(instr, "ldm'cond'pu 'rn'w, 'rlist" ); |
| 979 | } else { |
| 980 | Format(instr, "stm'cond'pu 'rn'w, 'rlist" ); |
| 981 | } |
| 982 | } |
| 983 | |
| 984 | void ARMDecoder::DecodeType5(Instr* instr) { |
| 985 | Format(instr, "b'l'cond 'target ; 'dest" ); |
| 986 | } |
| 987 | |
| 988 | void ARMDecoder::DecodeType6(Instr* instr) { |
| 989 | if (instr->IsVFPDoubleTransfer()) { |
| 990 | if (instr->Bit(8) == 0) { |
| 991 | if (instr->Bit(20) == 1) { |
| 992 | Format(instr, "vmovrrs'cond 'rd, 'rn, {'sm, 'sm1}" ); |
| 993 | } else { |
| 994 | Format(instr, "vmovsrr'cond {'sm, 'sm1}, 'rd, 'rn" ); |
| 995 | } |
| 996 | } else { |
| 997 | if (instr->Bit(20) == 1) { |
| 998 | Format(instr, "vmovrrd'cond 'rd, 'rn, 'dm" ); |
| 999 | } else { |
| 1000 | Format(instr, "vmovdrr'cond 'dm, 'rd, 'rn" ); |
| 1001 | } |
| 1002 | } |
| 1003 | } else if (instr->IsVFPLoadStore()) { |
| 1004 | if (instr->Bit(8) == 0) { |
| 1005 | if (instr->Bit(20) == 1) { // vldrs |
| 1006 | if (instr->Bit(23) == 1) { |
| 1007 | Format(instr, "vldrs'cond 'sd, ['rn, #+'off10]" ); |
| 1008 | } else { |
| 1009 | Format(instr, "vldrs'cond 'sd, ['rn, #-'off10]" ); |
| 1010 | } |
| 1011 | } else { // vstrs |
| 1012 | if (instr->Bit(23) == 1) { |
| 1013 | Format(instr, "vstrs'cond 'sd, ['rn, #+'off10]" ); |
| 1014 | } else { |
| 1015 | Format(instr, "vstrs'cond 'sd, ['rn, #-'off10]" ); |
| 1016 | } |
| 1017 | } |
| 1018 | } else { |
| 1019 | if (instr->Bit(20) == 1) { // vldrd |
| 1020 | if (instr->Bit(23) == 1) { |
| 1021 | Format(instr, "vldrd'cond 'dd, ['rn, #+'off10]" ); |
| 1022 | } else { |
| 1023 | Format(instr, "vldrd'cond 'dd, ['rn, #-'off10]" ); |
| 1024 | } |
| 1025 | } else { // vstrd |
| 1026 | if (instr->Bit(23) == 1) { |
| 1027 | Format(instr, "vstrd'cond 'dd, ['rn, #+'off10]" ); |
| 1028 | } else { |
| 1029 | Format(instr, "vstrd'cond 'dd, ['rn, #-'off10]" ); |
| 1030 | } |
| 1031 | } |
| 1032 | } |
| 1033 | } else if (instr->IsVFPMultipleLoadStore()) { |
| 1034 | if (instr->HasL()) { // vldm |
| 1035 | if (instr->Bit(8)) { // vldmd |
| 1036 | Format(instr, "vldmd'cond'pu 'rn'w, 'dlist" ); |
| 1037 | } else { // vldms |
| 1038 | Format(instr, "vldms'cond'pu 'rn'w, 'slist" ); |
| 1039 | } |
| 1040 | } else { // vstm |
| 1041 | if (instr->Bit(8)) { // vstmd |
| 1042 | Format(instr, "vstmd'cond'pu 'rn'w, 'dlist" ); |
| 1043 | } else { // vstms |
| 1044 | Format(instr, "vstms'cond'pu 'rn'w, 'slist" ); |
| 1045 | } |
| 1046 | } |
| 1047 | } else { |
| 1048 | Unknown(instr); |
| 1049 | } |
| 1050 | } |
| 1051 | |
| 1052 | void ARMDecoder::DecodeType7(Instr* instr) { |
| 1053 | if (instr->Bit(24) == 1) { |
| 1054 | Format(instr, "svc'cond #'svc" ); |
| 1055 | } else if (instr->IsVFPDataProcessingOrSingleTransfer()) { |
| 1056 | if (instr->Bit(4) == 0) { |
| 1057 | // VFP Data Processing |
| 1058 | switch (instr->Bits(20, 4) & 0xb) { |
| 1059 | case 0: { // vmla, vmls floating-point |
| 1060 | if (instr->Bit(8) == 0) { |
| 1061 | if (instr->Bit(6) == 0) { |
| 1062 | Format(instr, "vmlas'cond 'sd, 'sn, 'sm" ); |
| 1063 | } else { |
| 1064 | Format(instr, "vmlss'cond 'sd, 'sn, 'sm" ); |
| 1065 | } |
| 1066 | } else { |
| 1067 | if (instr->Bit(6) == 0) { |
| 1068 | Format(instr, "vmlad'cond 'dd, 'dn, 'dm" ); |
| 1069 | } else { |
| 1070 | Format(instr, "vmlsd'cond 'dd, 'dn, 'dm" ); |
| 1071 | } |
| 1072 | } |
| 1073 | break; |
| 1074 | } |
| 1075 | case 1: // vnmla, vnmls, vnmul |
| 1076 | default: { |
| 1077 | Unknown(instr); |
| 1078 | break; |
| 1079 | } |
| 1080 | case 2: { // vmul |
| 1081 | if (instr->Bit(8) == 0) { |
| 1082 | Format(instr, "vmuls'cond 'sd, 'sn, 'sm" ); |
| 1083 | } else { |
| 1084 | Format(instr, "vmuld'cond 'dd, 'dn, 'dm" ); |
| 1085 | } |
| 1086 | break; |
| 1087 | } |
| 1088 | case 8: { // vdiv |
| 1089 | if (instr->Bit(8) == 0) { |
| 1090 | Format(instr, "vdivs'cond 'sd, 'sn, 'sm" ); |
| 1091 | } else { |
| 1092 | Format(instr, "vdivd'cond 'dd, 'dn, 'dm" ); |
| 1093 | } |
| 1094 | break; |
| 1095 | } |
| 1096 | case 3: { // vadd, vsub floating-point |
| 1097 | if (instr->Bit(8) == 0) { |
| 1098 | if (instr->Bit(6) == 0) { |
| 1099 | Format(instr, "vadds'cond 'sd, 'sn, 'sm" ); |
| 1100 | } else { |
| 1101 | Format(instr, "vsubs'cond 'sd, 'sn, 'sm" ); |
| 1102 | } |
| 1103 | } else { |
| 1104 | if (instr->Bit(6) == 0) { |
| 1105 | Format(instr, "vaddd'cond 'dd, 'dn, 'dm" ); |
| 1106 | } else { |
| 1107 | Format(instr, "vsubd'cond 'dd, 'dn, 'dm" ); |
| 1108 | } |
| 1109 | } |
| 1110 | break; |
| 1111 | } |
| 1112 | case 0xb: { // Other VFP data-processing instructions |
| 1113 | if (instr->Bit(6) == 0) { // vmov immediate |
| 1114 | if (instr->Bit(8) == 0) { |
| 1115 | Format(instr, "vmovs'cond 'sd, #'immf" ); |
| 1116 | } else { |
| 1117 | Format(instr, "vmovd'cond 'dd, #'immd" ); |
| 1118 | } |
| 1119 | break; |
| 1120 | } |
| 1121 | switch (instr->Bits(16, 4)) { |
| 1122 | case 0: { // vmov register, vabs |
| 1123 | switch (instr->Bits(6, 2)) { |
| 1124 | case 1: { // vmov register |
| 1125 | if (instr->Bit(8) == 0) { |
| 1126 | Format(instr, "vmovs'cond 'sd, 'sm" ); |
| 1127 | } else { |
| 1128 | Format(instr, "vmovd'cond 'dd, 'dm" ); |
| 1129 | } |
| 1130 | break; |
| 1131 | } |
| 1132 | case 3: { // vabs |
| 1133 | if (instr->Bit(8) == 0) { |
| 1134 | Format(instr, "vabss'cond 'sd, 'sm" ); |
| 1135 | } else { |
| 1136 | Format(instr, "vabsd'cond 'dd, 'dm" ); |
| 1137 | } |
| 1138 | break; |
| 1139 | } |
| 1140 | default: { |
| 1141 | Unknown(instr); |
| 1142 | break; |
| 1143 | } |
| 1144 | } |
| 1145 | break; |
| 1146 | } |
| 1147 | case 1: { // vneg, vsqrt |
| 1148 | switch (instr->Bits(6, 2)) { |
| 1149 | case 1: { // vneg |
| 1150 | if (instr->Bit(8) == 0) { |
| 1151 | Format(instr, "vnegs'cond 'sd, 'sm" ); |
| 1152 | } else { |
| 1153 | Format(instr, "vnegd'cond 'dd, 'dm" ); |
| 1154 | } |
| 1155 | break; |
| 1156 | } |
| 1157 | case 3: { // vsqrt |
| 1158 | if (instr->Bit(8) == 0) { |
| 1159 | Format(instr, "vsqrts'cond 'sd, 'sm" ); |
| 1160 | } else { |
| 1161 | Format(instr, "vsqrtd'cond 'dd, 'dm" ); |
| 1162 | } |
| 1163 | break; |
| 1164 | } |
| 1165 | default: { |
| 1166 | Unknown(instr); |
| 1167 | break; |
| 1168 | } |
| 1169 | } |
| 1170 | break; |
| 1171 | } |
| 1172 | case 4: // vcmp, vcmpe |
| 1173 | case 5: { // vcmp #0.0, vcmpe #0.0 |
| 1174 | if (instr->Bit(7) == 1) { // vcmpe |
| 1175 | Unknown(instr); |
| 1176 | } else { |
| 1177 | if (instr->Bit(8) == 0) { // vcmps |
| 1178 | if (instr->Bit(16) == 0) { |
| 1179 | Format(instr, "vcmps'cond 'sd, 'sm" ); |
| 1180 | } else { |
| 1181 | Format(instr, "vcmps'cond 'sd, #0.0" ); |
| 1182 | } |
| 1183 | } else { // vcmpd |
| 1184 | if (instr->Bit(16) == 0) { |
| 1185 | Format(instr, "vcmpd'cond 'dd, 'dm" ); |
| 1186 | } else { |
| 1187 | Format(instr, "vcmpd'cond 'dd, #0.0" ); |
| 1188 | } |
| 1189 | } |
| 1190 | } |
| 1191 | break; |
| 1192 | } |
| 1193 | case 7: { // vcvt between double-precision and single-precision |
| 1194 | if (instr->Bit(8) == 0) { |
| 1195 | Format(instr, "vcvtds'cond 'dd, 'sm" ); |
| 1196 | } else { |
| 1197 | Format(instr, "vcvtsd'cond 'sd, 'dm" ); |
| 1198 | } |
| 1199 | break; |
| 1200 | } |
| 1201 | case 8: { // vcvt, vcvtr between floating-point and integer |
| 1202 | if (instr->Bit(8) == 0) { |
| 1203 | if (instr->Bit(7) == 0) { |
| 1204 | Format(instr, "vcvtsu'cond 'sd, 'sm" ); |
| 1205 | } else { |
| 1206 | Format(instr, "vcvtsi'cond 'sd, 'sm" ); |
| 1207 | } |
| 1208 | } else { |
| 1209 | if (instr->Bit(7) == 0) { |
| 1210 | Format(instr, "vcvtdu'cond 'dd, 'sm" ); |
| 1211 | } else { |
| 1212 | Format(instr, "vcvtdi'cond 'dd, 'sm" ); |
| 1213 | } |
| 1214 | } |
| 1215 | break; |
| 1216 | } |
| 1217 | case 12: |
| 1218 | case 13: { // vcvt, vcvtr between floating-point and integer |
| 1219 | if (instr->Bit(7) == 0) { |
| 1220 | // We only support round-to-zero mode |
| 1221 | Unknown(instr); |
| 1222 | break; |
| 1223 | } |
| 1224 | if (instr->Bit(8) == 0) { |
| 1225 | if (instr->Bit(16) == 0) { |
| 1226 | Format(instr, "vcvtus'cond 'sd, 'sm" ); |
| 1227 | } else { |
| 1228 | Format(instr, "vcvtis'cond 'sd, 'sm" ); |
| 1229 | } |
| 1230 | } else { |
| 1231 | if (instr->Bit(16) == 0) { |
| 1232 | Format(instr, "vcvtud'cond 'sd, 'dm" ); |
| 1233 | } else { |
| 1234 | Format(instr, "vcvtid'cond 'sd, 'dm" ); |
| 1235 | } |
| 1236 | } |
| 1237 | break; |
| 1238 | } |
| 1239 | case 2: // vcvtb, vcvtt |
| 1240 | case 3: // vcvtb, vcvtt |
| 1241 | case 9: // undefined |
| 1242 | case 10: // vcvt between floating-point and fixed-point |
| 1243 | case 11: // vcvt between floating-point and fixed-point |
| 1244 | case 14: // vcvt between floating-point and fixed-point |
| 1245 | case 15: // vcvt between floating-point and fixed-point |
| 1246 | default: { |
| 1247 | Unknown(instr); |
| 1248 | break; |
| 1249 | } |
| 1250 | } |
| 1251 | } break; |
| 1252 | } |
| 1253 | } else { |
| 1254 | // 8, 16, or 32-bit Transfer between ARM Core and VFP |
| 1255 | if ((instr->Bits(21, 3) == 0) && (instr->Bit(8) == 0)) { |
| 1256 | if (instr->Bit(20) == 0) { |
| 1257 | Format(instr, "vmovs'cond 'sn, 'rd" ); |
| 1258 | } else { |
| 1259 | Format(instr, "vmovr'cond 'rd, 'sn" ); |
| 1260 | } |
| 1261 | } else if ((instr->Bits(22, 3) == 0) && (instr->Bit(20) == 0) && |
| 1262 | (instr->Bit(8) == 1) && (instr->Bits(5, 2) == 0)) { |
| 1263 | if (instr->Bit(21) == 0) { |
| 1264 | Format(instr, "vmovd'cond 'dn[0], 'rd" ); |
| 1265 | } else { |
| 1266 | Format(instr, "vmovd'cond 'dn[1], 'rd" ); |
| 1267 | } |
| 1268 | } else if ((instr->Bits(20, 4) == 0xf) && (instr->Bit(8) == 0)) { |
| 1269 | if (instr->Bits(12, 4) == 0xf) { |
| 1270 | Format(instr, "vmrs'cond APSR, FPSCR" ); |
| 1271 | } else { |
| 1272 | Format(instr, "vmrs'cond 'rd, FPSCR" ); |
| 1273 | } |
| 1274 | } else { |
| 1275 | Unknown(instr); |
| 1276 | } |
| 1277 | } |
| 1278 | } else { |
| 1279 | Unknown(instr); |
| 1280 | } |
| 1281 | } |
| 1282 | |
| 1283 | void ARMDecoder::DecodeSIMDDataProcessing(Instr* instr) { |
| 1284 | ASSERT(instr->ConditionField() == kSpecialCondition); |
| 1285 | if (instr->Bit(6) == 1) { |
| 1286 | if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 0) && |
| 1287 | (instr->Bits(23, 2) == 0)) { |
| 1288 | Format(instr, "vaddq'sz 'qd, 'qn, 'qm" ); |
| 1289 | } else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 0) && |
| 1290 | (instr->Bits(23, 2) == 0) && (instr->Bit(21) == 0)) { |
| 1291 | Format(instr, "vaddqs 'qd, 'qn, 'qm" ); |
| 1292 | } else if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 0) && |
| 1293 | (instr->Bits(23, 2) == 2)) { |
| 1294 | Format(instr, "vsubq'sz 'qd, 'qn, 'qm" ); |
| 1295 | } else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 0) && |
| 1296 | (instr->Bits(23, 2) == 0) && (instr->Bit(21) == 1)) { |
| 1297 | Format(instr, "vsubqs 'qd, 'qn, 'qm" ); |
| 1298 | } else if ((instr->Bits(8, 4) == 9) && (instr->Bit(4) == 1) && |
| 1299 | (instr->Bits(23, 2) == 0)) { |
| 1300 | Format(instr, "vmulq'sz 'qd, 'qn, 'qm" ); |
| 1301 | } else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 1) && |
| 1302 | (instr->Bits(23, 2) == 2) && (instr->Bit(21) == 0)) { |
| 1303 | Format(instr, "vmulqs 'qd, 'qn, 'qm" ); |
| 1304 | } else if ((instr->Bits(8, 4) == 4) && (instr->Bit(4) == 0) && |
| 1305 | (instr->Bits(23, 5) == 4)) { |
| 1306 | Format(instr, "vshlqi'sz 'qd, 'qm, 'qn" ); |
| 1307 | } else if ((instr->Bits(8, 4) == 4) && (instr->Bit(4) == 0) && |
| 1308 | (instr->Bits(23, 5) == 6)) { |
| 1309 | Format(instr, "vshlqu'sz 'qd, 'qm, 'qn" ); |
| 1310 | } else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) && |
| 1311 | (instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 2)) { |
| 1312 | Format(instr, "veorq 'qd, 'qn, 'qm" ); |
| 1313 | } else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) && |
| 1314 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 0)) { |
| 1315 | Format(instr, "vornq 'qd, 'qn, 'qm" ); |
| 1316 | } else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) && |
| 1317 | (instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) { |
| 1318 | if (instr->QmField() == instr->QnField()) { |
| 1319 | Format(instr, "vmovq 'qd, 'qm" ); |
| 1320 | } else { |
| 1321 | Format(instr, "vorrq 'qd, 'qm" ); |
| 1322 | } |
| 1323 | } else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) && |
| 1324 | (instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) { |
| 1325 | Format(instr, "vandq 'qd, 'qn, 'qm" ); |
| 1326 | } else if ((instr->Bits(7, 5) == 11) && (instr->Bit(4) == 0) && |
| 1327 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 5) == 7) && |
| 1328 | (instr->Bits(16, 4) == 0)) { |
| 1329 | Format(instr, "vmvnq 'qd, 'qm" ); |
| 1330 | } else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) && |
| 1331 | (instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) { |
| 1332 | Format(instr, "vminqs 'qd, 'qn, 'qm" ); |
| 1333 | } else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) && |
| 1334 | (instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) { |
| 1335 | Format(instr, "vmaxqs 'qd, 'qn, 'qm" ); |
| 1336 | } else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) && |
| 1337 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) && |
| 1338 | (instr->Bit(7) == 0) && (instr->Bits(16, 4) == 9)) { |
| 1339 | Format(instr, "vabsqs 'qd, 'qm" ); |
| 1340 | } else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) && |
| 1341 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) && |
| 1342 | (instr->Bit(7) == 1) && (instr->Bits(16, 4) == 9)) { |
| 1343 | Format(instr, "vnegqs 'qd, 'qm" ); |
| 1344 | } else if ((instr->Bits(7, 5) == 10) && (instr->Bit(4) == 0) && |
| 1345 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) && |
| 1346 | (instr->Bits(16, 4) == 11)) { |
| 1347 | Format(instr, "vrecpeqs 'qd, 'qm" ); |
| 1348 | } else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 1) && |
| 1349 | (instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) { |
| 1350 | Format(instr, "vrecpsqs 'qd, 'qn, 'qm" ); |
| 1351 | } else if ((instr->Bits(8, 4) == 5) && (instr->Bit(4) == 0) && |
| 1352 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) && |
| 1353 | (instr->Bit(7) == 1) && (instr->Bits(16, 4) == 11)) { |
| 1354 | Format(instr, "vrsqrteqs 'qd, 'qm" ); |
| 1355 | } else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 1) && |
| 1356 | (instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) { |
| 1357 | Format(instr, "vrsqrtsqs 'qd, 'qn, 'qm" ); |
| 1358 | } else if ((instr->Bits(8, 4) == 12) && (instr->Bit(4) == 0) && |
| 1359 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) && |
| 1360 | (instr->Bit(7) == 0)) { |
| 1361 | int32_t imm4 = instr->Bits(16, 4); |
| 1362 | if (imm4 & 1) { |
| 1363 | Format(instr, "vdupb 'qd, 'dm['imm4_vdup]" ); |
| 1364 | } else if (imm4 & 2) { |
| 1365 | Format(instr, "vduph 'qd, 'dm['imm4_vdup]" ); |
| 1366 | } else if (imm4 & 4) { |
| 1367 | Format(instr, "vdupw 'qd, 'dm['imm4_vdup]" ); |
| 1368 | } else { |
| 1369 | Unknown(instr); |
| 1370 | } |
| 1371 | } else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 0) && |
| 1372 | (instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) && |
| 1373 | (instr->Bit(7) == 1) && (instr->Bits(16, 4) == 10)) { |
| 1374 | Format(instr, "vzipqw 'qd, 'qm" ); |
| 1375 | } else if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 1) && |
| 1376 | (instr->Bits(23, 2) == 2)) { |
| 1377 | Format(instr, "vceqq'sz 'qd, 'qn, 'qm" ); |
| 1378 | } else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) && |
| 1379 | (instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) { |
| 1380 | Format(instr, "vceqqs 'qd, 'qn, 'qm" ); |
| 1381 | } else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 1) && |
| 1382 | (instr->Bits(23, 2) == 0)) { |
| 1383 | Format(instr, "vcgeq'sz 'qd, 'qn, 'qm" ); |
| 1384 | } else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 1) && |
| 1385 | (instr->Bits(23, 2) == 2)) { |
| 1386 | Format(instr, "vcugeq'sz 'qd, 'qn, 'qm" ); |
| 1387 | } else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) && |
| 1388 | (instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 2)) { |
| 1389 | Format(instr, "vcgeqs 'qd, 'qn, 'qm" ); |
| 1390 | } else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 0) && |
| 1391 | (instr->Bits(23, 2) == 0)) { |
| 1392 | Format(instr, "vcgtq'sz 'qd, 'qn, 'qm" ); |
| 1393 | } else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 0) && |
| 1394 | (instr->Bits(23, 2) == 2)) { |
| 1395 | Format(instr, "vcugtq'sz 'qd, 'qn, 'qm" ); |
| 1396 | } else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) && |
| 1397 | (instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 2)) { |
| 1398 | Format(instr, "vcgtqs 'qd, 'qn, 'qm" ); |
| 1399 | } else { |
| 1400 | Unknown(instr); |
| 1401 | } |
| 1402 | } else { |
| 1403 | if ((instr->Bits(23, 2) == 3) && (instr->Bits(20, 2) == 3) && |
| 1404 | (instr->Bits(10, 2) == 2) && (instr->Bit(4) == 0)) { |
| 1405 | Format(instr, "vtbl 'dd, 'dtbllist, 'dm" ); |
| 1406 | } else { |
| 1407 | Unknown(instr); |
| 1408 | } |
| 1409 | } |
| 1410 | } |
| 1411 | |
| 1412 | void ARMDecoder::InstructionDecode(uword pc) { |
| 1413 | Instr* instr = Instr::At(pc); |
| 1414 | |
| 1415 | if (instr->ConditionField() == kSpecialCondition) { |
| 1416 | if (instr->InstructionBits() == static_cast<int32_t>(0xf57ff01f)) { |
| 1417 | Format(instr, "clrex" ); |
| 1418 | } else if (instr->InstructionBits() == |
| 1419 | static_cast<int32_t>(kDataMemoryBarrier)) { |
| 1420 | Format(instr, "dmb ish" ); |
| 1421 | } else { |
| 1422 | if (instr->IsSIMDDataProcessing()) { |
| 1423 | DecodeSIMDDataProcessing(instr); |
| 1424 | } else { |
| 1425 | Unknown(instr); |
| 1426 | } |
| 1427 | } |
| 1428 | } else { |
| 1429 | switch (instr->TypeField()) { |
| 1430 | case 0: |
| 1431 | case 1: { |
| 1432 | DecodeType01(instr); |
| 1433 | break; |
| 1434 | } |
| 1435 | case 2: { |
| 1436 | DecodeType2(instr); |
| 1437 | break; |
| 1438 | } |
| 1439 | case 3: { |
| 1440 | DecodeType3(instr); |
| 1441 | break; |
| 1442 | } |
| 1443 | case 4: { |
| 1444 | DecodeType4(instr); |
| 1445 | break; |
| 1446 | } |
| 1447 | case 5: { |
| 1448 | DecodeType5(instr); |
| 1449 | break; |
| 1450 | } |
| 1451 | case 6: { |
| 1452 | DecodeType6(instr); |
| 1453 | break; |
| 1454 | } |
| 1455 | case 7: { |
| 1456 | DecodeType7(instr); |
| 1457 | break; |
| 1458 | } |
| 1459 | default: { |
| 1460 | // The type field is 3-bits in the ARM encoding. |
| 1461 | UNREACHABLE(); |
| 1462 | break; |
| 1463 | } |
| 1464 | } |
| 1465 | } |
| 1466 | } |
| 1467 | |
| 1468 | void Disassembler::DecodeInstruction(char* hex_buffer, |
| 1469 | intptr_t hex_size, |
| 1470 | char* human_buffer, |
| 1471 | intptr_t human_size, |
| 1472 | int* out_instr_size, |
| 1473 | const Code& code, |
| 1474 | Object** object, |
| 1475 | uword pc) { |
| 1476 | ARMDecoder decoder(human_buffer, human_size); |
| 1477 | decoder.InstructionDecode(pc); |
| 1478 | int32_t instruction_bits = Instr::At(pc)->InstructionBits(); |
| 1479 | Utils::SNPrint(hex_buffer, hex_size, "%08x" , instruction_bits); |
| 1480 | if (out_instr_size) { |
| 1481 | *out_instr_size = Instr::kInstrSize; |
| 1482 | } |
| 1483 | |
| 1484 | *object = NULL; |
| 1485 | // TODO(36839): Make DecodeLoadObjectFromPoolOrThread work on simarm_x64. |
| 1486 | #if !defined(IS_SIMARM_X64) |
| 1487 | if (!code.IsNull()) { |
| 1488 | *object = &Object::Handle(); |
| 1489 | if (!DecodeLoadObjectFromPoolOrThread(pc, code, *object)) { |
| 1490 | *object = NULL; |
| 1491 | } |
| 1492 | } |
| 1493 | #endif // !defined(IS_SIMARM_X64) |
| 1494 | } |
| 1495 | |
| 1496 | #endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER) |
| 1497 | |
| 1498 | } // namespace dart |
| 1499 | |
| 1500 | #endif // defined(TARGET_ARCH_ARM) |
| 1501 | |