Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/disassembler.h" | 5 #include "vm/disassembler.h" |
| 6 | 6 |
| 7 #include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. | 7 #include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. |
| 8 #if defined(TARGET_ARCH_ARM) | 8 #if defined(TARGET_ARCH_ARM) |
| 9 #include "platform/assert.h" | 9 #include "platform/assert.h" |
| 10 | 10 |
| 11 namespace dart { | 11 namespace dart { |
| 12 | 12 |
| 13 class ARMDecoder : public ValueObject { | 13 class ARMDecoder : public ValueObject { |
| 14 public: | 14 public: |
| 15 ARMDecoder(char* buffer, size_t buffer_size) | 15 ARMDecoder(char* buffer, size_t buffer_size) |
| 16 : buffer_(buffer), | 16 : buffer_(buffer), |
| 17 buffer_size_(buffer_size), | 17 buffer_size_(buffer_size), |
| 18 buffer_pos_(0) { | 18 buffer_pos_(0) { |
| 19 buffer_[buffer_pos_] = '\0'; | 19 buffer_[buffer_pos_] = '\0'; |
| 20 } | 20 } |
| 21 | 21 |
| 22 ~ARMDecoder() {} | 22 ~ARMDecoder() {} |
| 23 | 23 |
| 24 // Writes one disassembled instruction into 'buffer' (0-terminated). | 24 // Writes one disassembled instruction into 'buffer' (0-terminated). |
| 25 void InstructionDecode(uword pc); | 25 // Returns true if the instruction was successfully decoded, false otherwise. |
| 26 bool InstructionDecode(uword pc); | |
| 26 | 27 |
| 27 private: | 28 private: |
| 28 // Bottleneck functions to print into the out_buffer. | 29 // Bottleneck functions to print into the out_buffer. |
| 29 void Print(const char* str); | 30 void Print(const char* str); |
| 30 | 31 |
| 31 // Printing of common values. | 32 // Printing of common values. |
| 32 void PrintRegister(int reg); | 33 void PrintRegister(int reg); |
| 33 void PrintSRegister(int reg); | 34 void PrintSRegister(int reg); |
| 34 void PrintDRegister(int reg); | 35 void PrintDRegister(int reg); |
| 35 void PrintCondition(Instr* instr); | 36 void PrintCondition(Instr* instr); |
| 36 void PrintShiftRm(Instr* instr); | 37 void PrintShiftRm(Instr* instr); |
| 37 void PrintShiftImm(Instr* instr); | 38 void PrintShiftImm(Instr* instr); |
| 38 void PrintPU(Instr* instr); | 39 void PrintPU(Instr* instr); |
| 39 | 40 |
| 40 // Handle formatting of instructions and their options. | 41 // Handle formatting of instructions and their options. |
| 41 int FormatRegister(Instr* instr, const char* option); | 42 int FormatRegister(Instr* instr, const char* option); |
| 42 int FormatSRegister(Instr* instr, const char* option); | 43 int FormatSRegister(Instr* instr, const char* option); |
| 43 int FormatDRegister(Instr* instr, const char* option); | 44 int FormatDRegister(Instr* instr, const char* option); |
| 44 int FormatOption(Instr* instr, const char* option); | 45 int FormatOption(Instr* instr, const char* option); |
| 45 void Format(Instr* instr, const char* format); | 46 void Format(Instr* instr, const char* format); |
| 46 void Unknown(Instr* instr); | 47 void Unknown(Instr* instr); |
| 47 | 48 |
| 48 // Each of these functions decodes one particular instruction type, a 3-bit | 49 // Each of these functions decodes one particular instruction type, a 3-bit |
| 49 // field in the instruction encoding. | 50 // field in the instruction encoding. |
| 50 // Types 0 and 1 are combined as they are largely the same except for the way | 51 // Types 0 and 1 are combined as they are largely the same except for the way |
| 51 // they interpret the shifter operand. | 52 // they interpret the shifter operand. |
| 52 void DecodeType01(Instr* instr); | 53 bool DecodeType01(Instr* instr); |
| 53 void DecodeType2(Instr* instr); | 54 bool DecodeType2(Instr* instr); |
| 54 void DecodeType3(Instr* instr); | 55 bool DecodeType3(Instr* instr); |
| 55 void DecodeType4(Instr* instr); | 56 bool DecodeType4(Instr* instr); |
| 56 void DecodeType5(Instr* instr); | 57 bool DecodeType5(Instr* instr); |
| 57 void DecodeType6(Instr* instr); | 58 bool DecodeType6(Instr* instr); |
| 58 void DecodeType7(Instr* instr); | 59 bool DecodeType7(Instr* instr); |
| 59 | 60 |
| 60 // Convenience functions. | 61 // Convenience functions. |
| 61 char* get_buffer() const { return buffer_; } | 62 char* get_buffer() const { return buffer_; } |
| 62 char* current_position_in_buffer() { return buffer_ + buffer_pos_; } | 63 char* current_position_in_buffer() { return buffer_ + buffer_pos_; } |
| 63 size_t remaining_size_in_buffer() { return buffer_size_ - buffer_pos_; } | 64 size_t remaining_size_in_buffer() { return buffer_size_ - buffer_pos_; } |
| 64 | 65 |
| 65 char* buffer_; // Decode instructions into this buffer. | 66 char* buffer_; // Decode instructions into this buffer. |
| 66 size_t buffer_size_; // The size of the character buffer. | 67 size_t buffer_size_; // The size of the character buffer. |
| 67 size_t buffer_pos_; // Current character position in buffer. | 68 size_t buffer_pos_; // Current character position in buffer. |
| 68 | 69 |
| (...skipping 499 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 568 } | 569 } |
| 569 | 570 |
| 570 | 571 |
| 571 // For currently unimplemented decodings the disassembler calls Unknown(instr) | 572 // For currently unimplemented decodings the disassembler calls Unknown(instr) |
| 572 // which will just print "unknown" of the instruction bits. | 573 // which will just print "unknown" of the instruction bits. |
| 573 void ARMDecoder::Unknown(Instr* instr) { | 574 void ARMDecoder::Unknown(Instr* instr) { |
| 574 Format(instr, "unknown"); | 575 Format(instr, "unknown"); |
| 575 } | 576 } |
| 576 | 577 |
| 577 | 578 |
| 578 void ARMDecoder::DecodeType01(Instr* instr) { | 579 bool ARMDecoder::DecodeType01(Instr* instr) { |
| 580 bool decoded = true; | |
| 581 | |
| 579 if (!instr->IsDataProcessing()) { | 582 if (!instr->IsDataProcessing()) { |
| 580 // miscellaneous, multiply, sync primitives, extra loads and stores. | 583 // miscellaneous, multiply, sync primitives, extra loads and stores. |
| 581 if (instr->IsMiscellaneous()) { | 584 if (instr->IsMiscellaneous()) { |
| 582 switch (instr->Bits(4, 3)) { | 585 switch (instr->Bits(4, 3)) { |
| 583 case 1: { | 586 case 1: { |
| 584 if (instr->Bits(21, 2) == 0x3) { | 587 if (instr->Bits(21, 2) == 0x3) { |
| 585 Format(instr, "clz'cond 'rd, 'rm"); | 588 Format(instr, "clz'cond 'rd, 'rm"); |
| 586 } else if (instr->Bits(21, 2) == 0x1) { | 589 } else if (instr->Bits(21, 2) == 0x1) { |
| 587 Format(instr, "bx'cond 'rm"); | 590 Format(instr, "bx'cond 'rm"); |
| 588 } else { | 591 } else { |
| 589 Unknown(instr); | 592 Unknown(instr); |
| 593 decoded = false; | |
| 590 } | 594 } |
| 591 break; | 595 break; |
| 592 } | 596 } |
| 593 case 3: { | 597 case 3: { |
| 594 if (instr->Bits(21, 2) == 0x1) { | 598 if (instr->Bits(21, 2) == 0x1) { |
| 595 Format(instr, "blx'cond 'rm"); | 599 Format(instr, "blx'cond 'rm"); |
| 596 } else { | 600 } else { |
| 597 // Could be inlined constant. | 601 // Could be inlined constant. |
| 598 Unknown(instr); | 602 Unknown(instr); |
| 603 decoded = false; | |
| 599 } | 604 } |
| 600 break; | 605 break; |
| 601 } | 606 } |
| 602 case 7: { | 607 case 7: { |
| 603 if (instr->Bits(21, 2) == 0x1) { | 608 if (instr->Bits(21, 2) == 0x1) { |
| 604 Format(instr, "bkpt'cond #'imm12_4"); | 609 Format(instr, "bkpt'cond #'imm12_4"); |
| 605 } else { | 610 } else { |
| 606 // Format(instr, "smc'cond"); | 611 // Format(instr, "smc'cond"); |
| 607 Unknown(instr); // Not used. | 612 Unknown(instr); // Not used. |
| 613 decoded = false; | |
| 608 } | 614 } |
| 609 break; | 615 break; |
| 610 } | 616 } |
| 611 default: { | 617 default: { |
| 612 Unknown(instr); // Not used. | 618 Unknown(instr); // Not used. |
| 619 decoded = false; | |
| 613 break; | 620 break; |
| 614 } | 621 } |
| 615 } | 622 } |
| 616 } else if (instr->IsMultiplyOrSyncPrimitive()) { | 623 } else if (instr->IsMultiplyOrSyncPrimitive()) { |
| 617 if (instr->Bit(24) == 0) { | 624 if (instr->Bit(24) == 0) { |
| 618 // multiply instructions | 625 // multiply instructions |
| 619 switch (instr->Bits(21, 3)) { | 626 switch (instr->Bits(21, 3)) { |
| 620 case 0: { | 627 case 0: { |
| 621 // Assembler registers rd, rn, rm are encoded as rn, rm, rs. | 628 // Assembler registers rd, rn, rm are encoded as rn, rm, rs. |
| 622 Format(instr, "mul'cond's 'rn, 'rm, 'rs"); | 629 Format(instr, "mul'cond's 'rn, 'rm, 'rs"); |
| 623 break; | 630 break; |
| 624 } | 631 } |
| 625 case 1: { | 632 case 1: { |
| 626 // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. | 633 // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. |
| 627 Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd"); | 634 Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd"); |
| 628 break; | 635 break; |
| 629 } | 636 } |
| 630 case 3: { | 637 case 3: { |
| 631 // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. | 638 // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. |
| 632 Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd"); | 639 Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd"); |
| 633 break; | 640 break; |
| 634 } | 641 } |
| 635 case 4: { | 642 case 4: { |
| 636 // Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. | 643 // Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. |
| 637 Format(instr, "umull'cond's 'rd, 'rn, 'rm, 'rs"); | 644 Format(instr, "umull'cond's 'rd, 'rn, 'rm, 'rs"); |
| 638 break; | 645 break; |
| 639 } | 646 } |
| 640 default: { | 647 default: { |
| 641 Unknown(instr); // Not used. | 648 Unknown(instr); // Not used. |
| 649 decoded = false; | |
| 642 break; | 650 break; |
| 643 } | 651 } |
| 644 } | 652 } |
| 645 } else { | 653 } else { |
| 646 // synchronization primitives | 654 // synchronization primitives |
| 647 switch (instr->Bits(20, 4)) { | 655 switch (instr->Bits(20, 4)) { |
| 648 case 8: { | 656 case 8: { |
| 649 Format(instr, "strex'cond 'rd, 'rm, ['rn]"); | 657 Format(instr, "strex'cond 'rd, 'rm, ['rn]"); |
| 650 break; | 658 break; |
| 651 } | 659 } |
| 652 case 9: { | 660 case 9: { |
| 653 Format(instr, "ldrex'cond 'rd, ['rn]"); | 661 Format(instr, "ldrex'cond 'rd, ['rn]"); |
| 654 break; | 662 break; |
| 655 } | 663 } |
| 656 default: { | 664 default: { |
| 657 Unknown(instr); // Not used. | 665 Unknown(instr); // Not used. |
| 666 decoded = false; | |
| 658 break; | 667 break; |
| 659 } | 668 } |
| 660 } | 669 } |
| 661 } | 670 } |
| 662 } else if (instr->Bit(25) == 1) { | 671 } else if (instr->Bit(25) == 1) { |
| 663 // 16-bit immediate loads, msr (immediate), and hints | 672 // 16-bit immediate loads, msr (immediate), and hints |
| 664 switch (instr->Bits(20, 5)) { | 673 switch (instr->Bits(20, 5)) { |
| 665 case 16: { | 674 case 16: { |
| 666 Format(instr, "movw'cond 'rd, #'imm4_12"); | 675 Format(instr, "movw'cond 'rd, #'imm4_12"); |
| 667 break; | 676 break; |
| 668 } | 677 } |
| 669 case 18: { | 678 case 18: { |
| 670 if ((instr->Bits(16, 4) == 0) && (instr->Bits(0, 8) == 0)) { | 679 if ((instr->Bits(16, 4) == 0) && (instr->Bits(0, 8) == 0)) { |
| 671 Format(instr, "nop'cond"); | 680 Format(instr, "nop'cond"); |
| 672 } else { | 681 } else { |
| 673 Unknown(instr); // Not used. | 682 Unknown(instr); // Not used. |
| 683 decoded = false; | |
| 674 } | 684 } |
| 675 break; | 685 break; |
| 676 } | 686 } |
| 677 case 20: { | 687 case 20: { |
| 678 Format(instr, "movt'cond 'rd, #'imm4_12"); | 688 Format(instr, "movt'cond 'rd, #'imm4_12"); |
| 679 break; | 689 break; |
| 680 } | 690 } |
| 681 default: { | 691 default: { |
| 682 Unknown(instr); // Not used. | 692 Unknown(instr); // Not used. |
| 693 decoded = false; | |
| 683 break; | 694 break; |
| 684 } | 695 } |
| 685 } | 696 } |
| 686 } else { | 697 } else { |
| 687 // extra load/store instructions | 698 // extra load/store instructions |
| 688 switch (instr->PUField()) { | 699 switch (instr->PUField()) { |
| 689 case 0: { | 700 case 0: { |
| 690 if (instr->Bit(22) == 0) { | 701 if (instr->Bit(22) == 0) { |
| 691 Format(instr, "'memop'cond'x 'rd2, ['rn], -'rm"); | 702 Format(instr, "'memop'cond'x 'rd2, ['rn], -'rm"); |
| 692 } else { | 703 } else { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 757 } | 768 } |
| 758 case RSC: { | 769 case RSC: { |
| 759 Format(instr, "rsc'cond's 'rd, 'rn, 'shift_op"); | 770 Format(instr, "rsc'cond's 'rd, 'rn, 'shift_op"); |
| 760 break; | 771 break; |
| 761 } | 772 } |
| 762 case TST: { | 773 case TST: { |
| 763 if (instr->HasS()) { | 774 if (instr->HasS()) { |
| 764 Format(instr, "tst'cond 'rn, 'shift_op"); | 775 Format(instr, "tst'cond 'rn, 'shift_op"); |
| 765 } else { | 776 } else { |
| 766 Unknown(instr); // Not used. | 777 Unknown(instr); // Not used. |
| 778 decoded = false; | |
| 767 } | 779 } |
| 768 break; | 780 break; |
| 769 } | 781 } |
| 770 case TEQ: { | 782 case TEQ: { |
| 771 if (instr->HasS()) { | 783 if (instr->HasS()) { |
| 772 Format(instr, "teq'cond 'rn, 'shift_op"); | 784 Format(instr, "teq'cond 'rn, 'shift_op"); |
| 773 } else { | 785 } else { |
| 774 Unknown(instr); // Not used. | 786 Unknown(instr); // Not used. |
| 787 decoded = false; | |
| 775 } | 788 } |
| 776 break; | 789 break; |
| 777 } | 790 } |
| 778 case CMP: { | 791 case CMP: { |
| 779 if (instr->HasS()) { | 792 if (instr->HasS()) { |
| 780 Format(instr, "cmp'cond 'rn, 'shift_op"); | 793 Format(instr, "cmp'cond 'rn, 'shift_op"); |
| 781 } else { | 794 } else { |
| 782 Unknown(instr); // Not used. | 795 Unknown(instr); // Not used. |
| 796 decoded = false; | |
| 783 } | 797 } |
| 784 break; | 798 break; |
| 785 } | 799 } |
| 786 case CMN: { | 800 case CMN: { |
| 787 if (instr->HasS()) { | 801 if (instr->HasS()) { |
| 788 Format(instr, "cmn'cond 'rn, 'shift_op"); | 802 Format(instr, "cmn'cond 'rn, 'shift_op"); |
| 789 } else { | 803 } else { |
| 790 Unknown(instr); // Not used. | 804 Unknown(instr); // Not used. |
| 805 decoded = false; | |
| 791 } | 806 } |
| 792 break; | 807 break; |
| 793 } | 808 } |
| 794 case ORR: { | 809 case ORR: { |
| 795 Format(instr, "orr'cond's 'rd, 'rn, 'shift_op"); | 810 Format(instr, "orr'cond's 'rd, 'rn, 'shift_op"); |
| 796 break; | 811 break; |
| 797 } | 812 } |
| 798 case MOV: { | 813 case MOV: { |
| 799 Format(instr, "mov'cond's 'rd, 'shift_op"); | 814 Format(instr, "mov'cond's 'rd, 'shift_op"); |
| 800 break; | 815 break; |
| 801 } | 816 } |
| 802 case BIC: { | 817 case BIC: { |
| 803 Format(instr, "bic'cond's 'rd, 'rn, 'shift_op"); | 818 Format(instr, "bic'cond's 'rd, 'rn, 'shift_op"); |
| 804 break; | 819 break; |
| 805 } | 820 } |
| 806 case MVN: { | 821 case MVN: { |
| 807 Format(instr, "mvn'cond's 'rd, 'shift_op"); | 822 Format(instr, "mvn'cond's 'rd, 'shift_op"); |
| 808 break; | 823 break; |
| 809 } | 824 } |
| 810 default: { | 825 default: { |
| 811 // The Opcode field is a 4-bit field. | 826 // The Opcode field is a 4-bit field. |
| 812 UNREACHABLE(); | 827 UNREACHABLE(); |
| 813 break; | 828 break; |
| 814 } | 829 } |
| 815 } | 830 } |
| 816 } | 831 } |
| 832 | |
| 833 return decoded; | |
| 817 } | 834 } |
| 818 | 835 |
| 819 | 836 |
| 820 void ARMDecoder::DecodeType2(Instr* instr) { | 837 bool ARMDecoder::DecodeType2(Instr* instr) { |
| 838 bool decoded = true; | |
| 839 | |
| 821 switch (instr->PUField()) { | 840 switch (instr->PUField()) { |
| 822 case 0: { | 841 case 0: { |
| 823 if (instr->HasW()) { | 842 if (instr->HasW()) { |
| 824 Unknown(instr); // Not used. | 843 Unknown(instr); // Not used. |
| 844 decoded = false; | |
| 825 } else { | 845 } else { |
| 826 Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); | 846 Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); |
| 827 } | 847 } |
| 828 break; | 848 break; |
| 829 } | 849 } |
| 830 case 1: { | 850 case 1: { |
| 831 if (instr->HasW()) { | 851 if (instr->HasW()) { |
| 832 Unknown(instr); // Not used. | 852 Unknown(instr); // Not used. |
| 853 decoded = false; | |
| 833 } else { | 854 } else { |
| 834 Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); | 855 Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); |
| 835 } | 856 } |
| 836 break; | 857 break; |
| 837 } | 858 } |
| 838 case 2: { | 859 case 2: { |
| 839 Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); | 860 Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); |
| 840 break; | 861 break; |
| 841 } | 862 } |
| 842 case 3: { | 863 case 3: { |
| 843 Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); | 864 Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); |
| 844 break; | 865 break; |
| 845 } | 866 } |
| 846 default: { | 867 default: { |
| 847 // The PU field is a 2-bit field. | 868 // The PU field is a 2-bit field. |
| 848 UNREACHABLE(); | 869 UNREACHABLE(); |
| 849 break; | 870 break; |
| 850 } | 871 } |
| 851 } | 872 } |
| 873 | |
| 874 return decoded; | |
| 852 } | 875 } |
| 853 | 876 |
| 854 | 877 |
| 855 void ARMDecoder::DecodeType3(Instr* instr) { | 878 bool ARMDecoder::DecodeType3(Instr* instr) { |
| 879 bool decoded = true; | |
| 880 | |
| 856 if (instr->IsDivision()) { | 881 if (instr->IsDivision()) { |
| 857 if (instr->Bit(21)) { | 882 if (instr->Bit(21)) { |
| 858 Format(instr, "udiv'cond 'rd, 'rn, 'rm"); | 883 Format(instr, "udiv'cond 'rd, 'rn, 'rm"); |
| 859 } else { | 884 } else { |
| 860 Format(instr, "sdiv'cond 'rd, 'rn, 'rm"); | 885 Format(instr, "sdiv'cond 'rd, 'rn, 'rm"); |
| 861 } | 886 } |
| 862 return; | 887 return decoded; |
| 863 } | 888 } |
| 864 switch (instr->PUField()) { | 889 switch (instr->PUField()) { |
| 865 case 0: { | 890 case 0: { |
| 866 if (instr->HasW()) { | 891 if (instr->HasW()) { |
| 867 Unknown(instr); | 892 Unknown(instr); |
| 893 decoded = false; | |
| 868 } else { | 894 } else { |
| 869 Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); | 895 Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); |
| 870 } | 896 } |
| 871 break; | 897 break; |
| 872 } | 898 } |
| 873 case 1: { | 899 case 1: { |
| 874 if (instr->HasW()) { | 900 if (instr->HasW()) { |
| 875 Unknown(instr); | 901 Unknown(instr); |
| 902 decoded = false; | |
| 876 } else { | 903 } else { |
| 877 Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm"); | 904 Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm"); |
| 878 } | 905 } |
| 879 break; | 906 break; |
| 880 } | 907 } |
| 881 case 2: { | 908 case 2: { |
| 882 Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); | 909 Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); |
| 883 break; | 910 break; |
| 884 } | 911 } |
| 885 case 3: { | 912 case 3: { |
| 886 Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); | 913 Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); |
| 887 break; | 914 break; |
| 888 } | 915 } |
| 889 default: { | 916 default: { |
| 890 // The PU field is a 2-bit field. | 917 // The PU field is a 2-bit field. |
| 891 UNREACHABLE(); | 918 UNREACHABLE(); |
| 892 break; | 919 break; |
| 893 } | 920 } |
| 894 } | 921 } |
| 922 | |
| 923 return decoded; | |
| 895 } | 924 } |
| 896 | 925 |
| 897 | 926 |
| 898 void ARMDecoder::DecodeType4(Instr* instr) { | 927 bool ARMDecoder::DecodeType4(Instr* instr) { |
| 928 bool decoded = true; | |
| 899 if (instr->Bit(22) == 1) { | 929 if (instr->Bit(22) == 1) { |
| 900 Unknown(instr); // Privileged mode currently not supported. | 930 Unknown(instr); // Privileged mode currently not supported. |
| 931 decoded = false; | |
| 901 } else if (instr->HasL()) { | 932 } else if (instr->HasL()) { |
| 902 Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); | 933 Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); |
| 903 } else { | 934 } else { |
| 904 Format(instr, "stm'cond'pu 'rn'w, 'rlist"); | 935 Format(instr, "stm'cond'pu 'rn'w, 'rlist"); |
| 905 } | 936 } |
| 937 return decoded; | |
| 906 } | 938 } |
| 907 | 939 |
| 908 | 940 |
| 909 void ARMDecoder::DecodeType5(Instr* instr) { | 941 bool ARMDecoder::DecodeType5(Instr* instr) { |
| 910 Format(instr, "b'l'cond 'target ; 'dest"); | 942 Format(instr, "b'l'cond 'target ; 'dest"); |
| 943 return true; | |
| 911 } | 944 } |
| 912 | 945 |
| 913 | 946 |
| 914 void ARMDecoder::DecodeType6(Instr* instr) { | 947 bool ARMDecoder::DecodeType6(Instr* instr) { |
| 948 bool decoded = true; | |
| 949 | |
| 915 if (instr->IsVFPDoubleTransfer()) { | 950 if (instr->IsVFPDoubleTransfer()) { |
| 916 if (instr->Bit(8) == 0) { | 951 if (instr->Bit(8) == 0) { |
| 917 if (instr->Bit(20) == 1) { | 952 if (instr->Bit(20) == 1) { |
| 918 Format(instr, "vmovrrs'cond 'rd, 'rn, {'sm, 'sm1}"); | 953 Format(instr, "vmovrrs'cond 'rd, 'rn, {'sm, 'sm1}"); |
| 919 } else { | 954 } else { |
| 920 Format(instr, "vmovsrr'cond {'sm, 'sm1}, 'rd, 'rn"); | 955 Format(instr, "vmovsrr'cond {'sm, 'sm1}, 'rd, 'rn"); |
| 921 } | 956 } |
| 922 } else { | 957 } else { |
| 923 if (instr->Bit(20) == 1) { | 958 if (instr->Bit(20) == 1) { |
| 924 Format(instr, "vmovrrd'cond 'rd, 'rn, 'dm"); | 959 Format(instr, "vmovrrd'cond 'rd, 'rn, 'dm"); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 965 } | 1000 } |
| 966 } else { // vstm | 1001 } else { // vstm |
| 967 if (instr->Bit(8)) { // vstmd | 1002 if (instr->Bit(8)) { // vstmd |
| 968 Format(instr, "vstmd'cond'pu 'rn'w, 'dlist"); | 1003 Format(instr, "vstmd'cond'pu 'rn'w, 'dlist"); |
| 969 } else { // vstms | 1004 } else { // vstms |
| 970 Format(instr, "vstms'cond'pu 'rn'w, 'slist"); | 1005 Format(instr, "vstms'cond'pu 'rn'w, 'slist"); |
| 971 } | 1006 } |
| 972 } | 1007 } |
| 973 } else { | 1008 } else { |
| 974 Unknown(instr); | 1009 Unknown(instr); |
| 1010 decoded = false; | |
| 975 } | 1011 } |
| 1012 | |
| 1013 return decoded; | |
| 976 } | 1014 } |
| 977 | 1015 |
| 978 | 1016 |
| 979 void ARMDecoder::DecodeType7(Instr* instr) { | 1017 bool ARMDecoder::DecodeType7(Instr* instr) { |
| 1018 bool decoded = true; | |
| 1019 | |
| 980 if (instr->Bit(24) == 1) { | 1020 if (instr->Bit(24) == 1) { |
| 981 Format(instr, "svc'cond #'svc"); | 1021 Format(instr, "svc'cond #'svc"); |
| 982 if (instr->SvcField() == kStopMessageSvcCode) { | 1022 if (instr->SvcField() == kStopMessageSvcCode) { |
| 983 const char* message = *reinterpret_cast<const char**>( | 1023 const char* message = *reinterpret_cast<const char**>( |
| 984 reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize); | 1024 reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize); |
| 985 buffer_pos_ += OS::SNPrint(current_position_in_buffer(), | 1025 buffer_pos_ += OS::SNPrint(current_position_in_buffer(), |
| 986 remaining_size_in_buffer(), | 1026 remaining_size_in_buffer(), |
| 987 " ; \"%s\"", | 1027 " ; \"%s\"", |
| 988 message); | 1028 message); |
| 989 } | 1029 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1003 Format(instr, "vmlad'cond 'dd, 'dn, 'dm"); | 1043 Format(instr, "vmlad'cond 'dd, 'dn, 'dm"); |
| 1004 } else { | 1044 } else { |
| 1005 Format(instr, "vmlsd'cond 'dd, 'dn, 'dm"); | 1045 Format(instr, "vmlsd'cond 'dd, 'dn, 'dm"); |
| 1006 } | 1046 } |
| 1007 } | 1047 } |
| 1008 break; | 1048 break; |
| 1009 } | 1049 } |
| 1010 case 1: // vnmla, vnmls, vnmul | 1050 case 1: // vnmla, vnmls, vnmul |
| 1011 default: { | 1051 default: { |
| 1012 Unknown(instr); | 1052 Unknown(instr); |
| 1053 decoded = false; | |
| 1013 break; | 1054 break; |
| 1014 } | 1055 } |
| 1015 case 2: { // vmul | 1056 case 2: { // vmul |
| 1016 if (instr->Bit(8) == 0) { | 1057 if (instr->Bit(8) == 0) { |
| 1017 Format(instr, "vmuls'cond 'sd, 'sn, 'sm"); | 1058 Format(instr, "vmuls'cond 'sd, 'sn, 'sm"); |
| 1018 } else { | 1059 } else { |
| 1019 Format(instr, "vmuld'cond 'dd, 'dn, 'dm"); | 1060 Format(instr, "vmuld'cond 'dd, 'dn, 'dm"); |
| 1020 } | 1061 } |
| 1021 break; | 1062 break; |
| 1022 } | 1063 } |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1067 case 3: { // vabs | 1108 case 3: { // vabs |
| 1068 if (instr->Bit(8) == 0) { | 1109 if (instr->Bit(8) == 0) { |
| 1069 Format(instr, "vabss'cond 'sd, 'sm"); | 1110 Format(instr, "vabss'cond 'sd, 'sm"); |
| 1070 } else { | 1111 } else { |
| 1071 Format(instr, "vabsd'cond 'dd, 'dm"); | 1112 Format(instr, "vabsd'cond 'dd, 'dm"); |
| 1072 } | 1113 } |
| 1073 break; | 1114 break; |
| 1074 } | 1115 } |
| 1075 default: { | 1116 default: { |
| 1076 Unknown(instr); | 1117 Unknown(instr); |
| 1118 decoded = false; | |
| 1077 break; | 1119 break; |
| 1078 } | 1120 } |
| 1079 } | 1121 } |
| 1080 break; | 1122 break; |
| 1081 } | 1123 } |
| 1082 case 1: { // vneg, vsqrt | 1124 case 1: { // vneg, vsqrt |
| 1083 switch (instr->Bits(6, 2)) { | 1125 switch (instr->Bits(6, 2)) { |
| 1084 case 1: { // vneg | 1126 case 1: { // vneg |
| 1085 if (instr->Bit(8) == 0) { | 1127 if (instr->Bit(8) == 0) { |
| 1086 Format(instr, "vnegs'cond 'sd, 'sm"); | 1128 Format(instr, "vnegs'cond 'sd, 'sm"); |
| 1087 } else { | 1129 } else { |
| 1088 Format(instr, "vnegd'cond 'dd, 'dm"); | 1130 Format(instr, "vnegd'cond 'dd, 'dm"); |
| 1089 } | 1131 } |
| 1090 break; | 1132 break; |
| 1091 } | 1133 } |
| 1092 case 3: { // vsqrt | 1134 case 3: { // vsqrt |
| 1093 if (instr->Bit(8) == 0) { | 1135 if (instr->Bit(8) == 0) { |
| 1094 Format(instr, "vsqrts'cond 'sd, 'sm"); | 1136 Format(instr, "vsqrts'cond 'sd, 'sm"); |
| 1095 } else { | 1137 } else { |
| 1096 Format(instr, "vsqrtd'cond 'dd, 'dm"); | 1138 Format(instr, "vsqrtd'cond 'dd, 'dm"); |
| 1097 } | 1139 } |
| 1098 break; | 1140 break; |
| 1099 } | 1141 } |
| 1100 default: { | 1142 default: { |
| 1101 Unknown(instr); | 1143 Unknown(instr); |
| 1144 decoded = false; | |
| 1102 break; | 1145 break; |
| 1103 } | 1146 } |
| 1104 } | 1147 } |
| 1105 break; | 1148 break; |
| 1106 } | 1149 } |
| 1107 case 4: // vcmp, vcmpe | 1150 case 4: // vcmp, vcmpe |
| 1108 case 5: { // vcmp #0.0, vcmpe #0.0 | 1151 case 5: { // vcmp #0.0, vcmpe #0.0 |
| 1109 if (instr->Bit(7) == 1) { // vcmpe | 1152 if (instr->Bit(7) == 1) { // vcmpe |
| 1110 Unknown(instr); | 1153 Unknown(instr); |
| 1154 decoded = false; | |
| 1111 } else { | 1155 } else { |
| 1112 if (instr->Bit(8) == 0) { // vcmps | 1156 if (instr->Bit(8) == 0) { // vcmps |
| 1113 if (instr->Bit(16) == 0) { | 1157 if (instr->Bit(16) == 0) { |
| 1114 Format(instr, "vcmps'cond 'sd, 'sm"); | 1158 Format(instr, "vcmps'cond 'sd, 'sm"); |
| 1115 } else { | 1159 } else { |
| 1116 Format(instr, "vcmps'cond 'sd, #0.0"); | 1160 Format(instr, "vcmps'cond 'sd, #0.0"); |
| 1117 } | 1161 } |
| 1118 } else { // vcmpd | 1162 } else { // vcmpd |
| 1119 if (instr->Bit(16) == 0) { | 1163 if (instr->Bit(16) == 0) { |
| 1120 Format(instr, "vcmpd'cond 'dd, 'dm"); | 1164 Format(instr, "vcmpd'cond 'dd, 'dm"); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 1147 Format(instr, "vcvtdi'cond 'dd, 'sm"); | 1191 Format(instr, "vcvtdi'cond 'dd, 'sm"); |
| 1148 } | 1192 } |
| 1149 } | 1193 } |
| 1150 break; | 1194 break; |
| 1151 } | 1195 } |
| 1152 case 12: | 1196 case 12: |
| 1153 case 13: { // vcvt, vcvtr between floating-point and integer | 1197 case 13: { // vcvt, vcvtr between floating-point and integer |
| 1154 if (instr->Bit(7) == 0) { | 1198 if (instr->Bit(7) == 0) { |
| 1155 // We only support round-to-zero mode | 1199 // We only support round-to-zero mode |
| 1156 Unknown(instr); | 1200 Unknown(instr); |
| 1201 decoded = false; | |
| 1157 break; | 1202 break; |
| 1158 } | 1203 } |
| 1159 if (instr->Bit(8) == 0) { | 1204 if (instr->Bit(8) == 0) { |
| 1160 if (instr->Bit(16) == 0) { | 1205 if (instr->Bit(16) == 0) { |
| 1161 Format(instr, "vcvtus'cond 'sd, 'sm"); | 1206 Format(instr, "vcvtus'cond 'sd, 'sm"); |
| 1162 } else { | 1207 } else { |
| 1163 Format(instr, "vcvtis'cond 'sd, 'sm"); | 1208 Format(instr, "vcvtis'cond 'sd, 'sm"); |
| 1164 } | 1209 } |
| 1165 } else { | 1210 } else { |
| 1166 if (instr->Bit(16) == 0) { | 1211 if (instr->Bit(16) == 0) { |
| 1167 Format(instr, "vcvtud'cond 'sd, 'dm"); | 1212 Format(instr, "vcvtud'cond 'sd, 'dm"); |
| 1168 } else { | 1213 } else { |
| 1169 Format(instr, "vcvtid'cond 'sd, 'dm"); | 1214 Format(instr, "vcvtid'cond 'sd, 'dm"); |
| 1170 } | 1215 } |
| 1171 } | 1216 } |
| 1172 break; | 1217 break; |
| 1173 } | 1218 } |
| 1174 case 2: // vcvtb, vcvtt | 1219 case 2: // vcvtb, vcvtt |
| 1175 case 3: // vcvtb, vcvtt | 1220 case 3: // vcvtb, vcvtt |
| 1176 case 9: // undefined | 1221 case 9: // undefined |
| 1177 case 10: // vcvt between floating-point and fixed-point | 1222 case 10: // vcvt between floating-point and fixed-point |
| 1178 case 11: // vcvt between floating-point and fixed-point | 1223 case 11: // vcvt between floating-point and fixed-point |
| 1179 case 14: // vcvt between floating-point and fixed-point | 1224 case 14: // vcvt between floating-point and fixed-point |
| 1180 case 15: // vcvt between floating-point and fixed-point | 1225 case 15: // vcvt between floating-point and fixed-point |
| 1181 default: { | 1226 default: { |
| 1182 Unknown(instr); | 1227 Unknown(instr); |
| 1228 decoded = false; | |
| 1183 break; | 1229 break; |
| 1184 } | 1230 } |
| 1185 } | 1231 } |
| 1186 } | 1232 } |
| 1187 break; | 1233 break; |
| 1188 } | 1234 } |
| 1189 } else { | 1235 } else { |
| 1190 // 8, 16, or 32-bit Transfer between ARM Core and VFP | 1236 // 8, 16, or 32-bit Transfer between ARM Core and VFP |
| 1191 if ((instr->Bits(21, 3) == 0) && (instr->Bit(8) == 0)) { | 1237 if ((instr->Bits(21, 3) == 0) && (instr->Bit(8) == 0)) { |
| 1192 if (instr->Bit(20) == 0) { | 1238 if (instr->Bit(20) == 0) { |
| 1193 Format(instr, "vmovs'cond 'sn, 'rd"); | 1239 Format(instr, "vmovs'cond 'sn, 'rd"); |
| 1194 } else { | 1240 } else { |
| 1195 Format(instr, "vmovr'cond 'rd, 'sn"); | 1241 Format(instr, "vmovr'cond 'rd, 'sn"); |
| 1196 } | 1242 } |
| 1197 } else if ((instr->Bits(20, 4) == 0xf) && (instr->Bit(8) == 0) && | 1243 } else if ((instr->Bits(20, 4) == 0xf) && (instr->Bit(8) == 0) && |
| 1198 (instr->Bits(12, 4) == 0xf)) { | 1244 (instr->Bits(12, 4) == 0xf)) { |
| 1199 Format(instr, "vmstat'cond"); | 1245 Format(instr, "vmstat'cond"); |
| 1200 } else { | 1246 } else { |
| 1201 Unknown(instr); | 1247 Unknown(instr); |
| 1248 decoded = false; | |
| 1202 } | 1249 } |
| 1203 } | 1250 } |
| 1204 } else if (instr->IsMrcIdIsar0()) { | 1251 } else if (instr->IsMrcIdIsar0()) { |
| 1205 Format(instr, "mrc'cond p15, 0, 'rd, c0, c2, 0"); | 1252 Format(instr, "mrc'cond p15, 0, 'rd, c0, c2, 0"); |
| 1206 } else { | 1253 } else { |
| 1207 Unknown(instr); | 1254 Unknown(instr); |
| 1255 decoded = false; | |
| 1208 } | 1256 } |
| 1257 | |
| 1258 return decoded; | |
| 1209 } | 1259 } |
| 1210 | 1260 |
| 1211 | 1261 |
| 1212 void ARMDecoder::InstructionDecode(uword pc) { | 1262 bool ARMDecoder::InstructionDecode(uword pc) { |
| 1213 Instr* instr = Instr::At(pc); | 1263 bool decoded = true; |
| 1264 Instr* instr = Instr::At(pc); | |
| 1265 | |
| 1214 if (instr->ConditionField() == kSpecialCondition) { | 1266 if (instr->ConditionField() == kSpecialCondition) { |
| 1215 if (instr->InstructionBits() == static_cast<int32_t>(0xf57ff01f)) { | 1267 if (instr->InstructionBits() == static_cast<int32_t>(0xf57ff01f)) { |
| 1216 Format(instr, "clrex"); | 1268 Format(instr, "clrex"); |
| 1217 } else { | 1269 } else { |
| 1218 Unknown(instr); | 1270 Unknown(instr); |
| 1271 decoded = false; | |
| 1219 } | 1272 } |
| 1220 } else { | 1273 } else { |
| 1221 switch (instr->TypeField()) { | 1274 switch (instr->TypeField()) { |
| 1222 case 0: | 1275 case 0: |
| 1223 case 1: { | 1276 case 1: { |
| 1224 DecodeType01(instr); | 1277 decoded = DecodeType01(instr); |
| 1225 break; | 1278 break; |
| 1226 } | 1279 } |
| 1227 case 2: { | 1280 case 2: { |
| 1228 DecodeType2(instr); | 1281 decoded = DecodeType2(instr); |
| 1229 break; | 1282 break; |
| 1230 } | 1283 } |
| 1231 case 3: { | 1284 case 3: { |
| 1232 DecodeType3(instr); | 1285 decoded = DecodeType3(instr); |
| 1233 break; | 1286 break; |
| 1234 } | 1287 } |
| 1235 case 4: { | 1288 case 4: { |
| 1236 DecodeType4(instr); | 1289 decoded = DecodeType4(instr); |
| 1237 break; | 1290 break; |
| 1238 } | 1291 } |
| 1239 case 5: { | 1292 case 5: { |
| 1240 DecodeType5(instr); | 1293 decoded = DecodeType5(instr); |
| 1241 break; | 1294 break; |
| 1242 } | 1295 } |
| 1243 case 6: { | 1296 case 6: { |
| 1244 DecodeType6(instr); | 1297 decoded = DecodeType6(instr); |
| 1245 break; | 1298 break; |
| 1246 } | 1299 } |
| 1247 case 7: { | 1300 case 7: { |
| 1248 DecodeType7(instr); | 1301 decoded = DecodeType7(instr); |
| 1249 break; | 1302 break; |
| 1250 } | 1303 } |
| 1251 default: { | 1304 default: { |
| 1252 // The type field is 3-bits in the ARM encoding. | 1305 // The type field is 3-bits in the ARM encoding. |
| 1253 UNREACHABLE(); | 1306 UNREACHABLE(); |
| 1254 break; | 1307 break; |
| 1255 } | 1308 } |
| 1256 } | 1309 } |
| 1257 } | 1310 } |
| 1311 | |
| 1312 return decoded; | |
| 1258 } | 1313 } |
| 1259 | 1314 |
| 1260 | 1315 |
| 1261 int Disassembler::DecodeInstruction(char* hex_buffer, intptr_t hex_size, | 1316 int Disassembler::DecodeInstruction(char* hex_buffer, intptr_t hex_size, |
| 1262 char* human_buffer, intptr_t human_size, | 1317 char* human_buffer, intptr_t human_size, |
| 1263 uword pc) { | 1318 uword pc) { |
| 1264 ARMDecoder decoder(human_buffer, human_size); | 1319 ARMDecoder decoder(human_buffer, human_size); |
| 1265 decoder.InstructionDecode(pc); | 1320 if (decoder.InstructionDecode(pc)) { |
| 1266 int32_t instruction_bits = Instr::At(pc)->InstructionBits(); | 1321 int32_t instruction_bits = Instr::At(pc)->InstructionBits(); |
| 1267 OS::SNPrint(hex_buffer, hex_size, "%08x", instruction_bits); | 1322 OS::SNPrint(hex_buffer, hex_size, "%08x", instruction_bits); |
| 1268 return Instr::kInstrSize; | 1323 return Instr::kInstrSize; |
| 1324 } else { | |
| 1325 return -Instr::kInstrSize; | |
|
regis
2013/03/12 20:53:19
Did you mean to return 0? I am worried about retur
| |
| 1326 } | |
| 1269 } | 1327 } |
| 1270 | 1328 |
| 1271 | 1329 |
| 1272 void Disassembler::Disassemble(uword start, | 1330 bool Disassembler::Disassemble(uword start, |
| 1273 uword end, | 1331 uword end, |
| 1274 DisassemblyFormatter* formatter, | 1332 DisassemblyFormatter* formatter, |
| 1275 const Code::Comments& comments) { | 1333 const Code::Comments& comments) { |
| 1276 ASSERT(formatter != NULL); | 1334 ASSERT(formatter != NULL); |
| 1335 bool success = true; | |
| 1277 char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form. | 1336 char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form. |
| 1278 char human_buffer[kUserReadableBufferSize]; // Human-readable instruction. | 1337 char human_buffer[kUserReadableBufferSize]; // Human-readable instruction. |
| 1279 uword pc = start; | 1338 uword pc = start; |
| 1280 intptr_t comment_finger = 0; | 1339 intptr_t comment_finger = 0; |
| 1281 while (pc < end) { | 1340 while (pc < end) { |
| 1282 const intptr_t offset = pc - start; | 1341 const intptr_t offset = pc - start; |
| 1283 while (comment_finger < comments.Length() && | 1342 while (comment_finger < comments.Length() && |
| 1284 comments.PCOffsetAt(comment_finger) <= offset) { | 1343 comments.PCOffsetAt(comment_finger) <= offset) { |
| 1285 formatter->Print( | 1344 formatter->Print( |
| 1286 " ;; %s\n", | 1345 " ;; %s\n", |
| 1287 String::Handle(comments.CommentAt(comment_finger)).ToCString()); | 1346 String::Handle(comments.CommentAt(comment_finger)).ToCString()); |
| 1288 comment_finger++; | 1347 comment_finger++; |
| 1289 } | 1348 } |
| 1290 int instruction_length = DecodeInstruction(hex_buffer, | 1349 int instruction_length = DecodeInstruction(hex_buffer, |
| 1291 sizeof(hex_buffer), | 1350 sizeof(hex_buffer), |
| 1292 human_buffer, | 1351 human_buffer, |
| 1293 sizeof(human_buffer), | 1352 sizeof(human_buffer), |
| 1294 pc); | 1353 pc); |
| 1295 formatter->ConsumeInstruction(hex_buffer, | 1354 if (instruction_length > 0) { |
| 1296 sizeof(hex_buffer), | 1355 formatter->ConsumeInstruction(hex_buffer, |
| 1297 human_buffer, | 1356 sizeof(hex_buffer), |
| 1298 sizeof(human_buffer), | 1357 human_buffer, |
| 1299 pc); | 1358 sizeof(human_buffer), |
| 1300 pc += instruction_length; | 1359 pc); |
| 1360 pc += instruction_length; | |
| 1361 } else { | |
| 1362 ASSERT(instruction_length < 0); | |
| 1363 success = false; | |
| 1364 pc += (-instruction_length); | |
|
regis
2013/03/12 20:53:19
I see. You encode success/failure in the returned
| |
| 1365 } | |
| 1301 } | 1366 } |
| 1367 | |
| 1368 return success; | |
| 1302 } | 1369 } |
| 1303 | 1370 |
| 1304 } // namespace dart | 1371 } // namespace dart |
| 1305 | 1372 |
| 1306 #endif // defined TARGET_ARCH_ARM | 1373 #endif // defined TARGET_ARCH_ARM |
| OLD | NEW |