1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_ 18 #define ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_ 19 20 #include "base/arena_object.h" 21 #include "dex_file.h" 22 #include "dex_file-inl.h" 23 #include "dex_instruction-inl.h" 24 25 namespace art { 26 27 class CodeItemIterator : public ValueObject { 28 public: 29 CodeItemIterator(const DexFile::CodeItem& code_item, uint32_t start_dex_pc = 0u) 30 : code_ptr_(code_item.insns_ + start_dex_pc), 31 code_end_(code_item.insns_ + code_item.insns_size_in_code_units_), 32 dex_pc_(start_dex_pc) {} 33 34 bool Done() const { return code_ptr_ >= code_end_; } 35 bool IsLast() const { return code_ptr_ + CurrentInstruction().SizeInCodeUnits() >= code_end_; } 36 37 const Instruction& CurrentInstruction() const { return *Instruction::At(code_ptr_); } 38 uint32_t CurrentDexPc() const { return dex_pc_; } 39 40 void Advance() { 41 DCHECK(!Done()); 42 size_t instruction_size = CurrentInstruction().SizeInCodeUnits(); 43 code_ptr_ += instruction_size; 44 dex_pc_ += instruction_size; 45 } 46 47 private: 48 const uint16_t* code_ptr_; 49 const uint16_t* const code_end_; 50 uint32_t dex_pc_; 51 52 DISALLOW_COPY_AND_ASSIGN(CodeItemIterator); 53 }; 54 55 class DexSwitchTable : public ValueObject { 56 public: 57 DexSwitchTable(const Instruction& instruction, uint32_t dex_pc) 58 : instruction_(instruction), 59 dex_pc_(dex_pc), 60 sparse_(instruction.Opcode() == Instruction::SPARSE_SWITCH) { 61 int32_t table_offset = instruction.VRegB_31t(); 62 const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset; 63 DCHECK_EQ(table[0], sparse_ ? static_cast<uint16_t>(Instruction::kSparseSwitchSignature) 64 : static_cast<uint16_t>(Instruction::kPackedSwitchSignature)); 65 num_entries_ = table[1]; 66 values_ = reinterpret_cast<const int32_t*>(&table[2]); 67 } 68 69 uint16_t GetNumEntries() const { 70 return num_entries_; 71 } 72 73 void CheckIndex(size_t index) const { 74 if (sparse_) { 75 // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order. 76 DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_)); 77 } else { 78 // In a packed table, we have the starting key and num_entries_ values. 79 DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_)); 80 } 81 } 82 83 int32_t GetEntryAt(size_t index) const { 84 CheckIndex(index); 85 return values_[index]; 86 } 87 88 uint32_t GetDexPcForIndex(size_t index) const { 89 CheckIndex(index); 90 return dex_pc_ + 91 (reinterpret_cast<const int16_t*>(values_ + index) - 92 reinterpret_cast<const int16_t*>(&instruction_)); 93 } 94 95 // Index of the first value in the table. 96 size_t GetFirstValueIndex() const { 97 if (sparse_) { 98 // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order. 99 return num_entries_; 100 } else { 101 // In a packed table, we have the starting key and num_entries_ values. 102 return 1; 103 } 104 } 105 106 bool IsSparse() const { return sparse_; } 107 108 bool ShouldBuildDecisionTree() { 109 return IsSparse() || GetNumEntries() <= kSmallSwitchThreshold; 110 } 111 112 private: 113 const Instruction& instruction_; 114 const uint32_t dex_pc_; 115 116 // Whether this is a sparse-switch table (or a packed-switch one). 117 const bool sparse_; 118 119 // This can't be const as it needs to be computed off of the given instruction, and complicated 120 // expressions in the initializer list seemed very ugly. 121 uint16_t num_entries_; 122 123 const int32_t* values_; 124 125 // The number of entries in a packed switch before we use a jump table or specified 126 // compare/jump series. 127 static constexpr uint16_t kSmallSwitchThreshold = 3; 128 129 DISALLOW_COPY_AND_ASSIGN(DexSwitchTable); 130 }; 131 132 class DexSwitchTableIterator { 133 public: 134 explicit DexSwitchTableIterator(const DexSwitchTable& table) 135 : table_(table), 136 num_entries_(static_cast<size_t>(table_.GetNumEntries())), 137 first_target_offset_(table_.GetFirstValueIndex()), 138 index_(0u) {} 139 140 bool Done() const { return index_ >= num_entries_; } 141 bool IsLast() const { return index_ == num_entries_ - 1; } 142 143 void Advance() { 144 DCHECK(!Done()); 145 index_++; 146 } 147 148 int32_t CurrentKey() const { 149 return table_.IsSparse() ? table_.GetEntryAt(index_) : table_.GetEntryAt(0) + index_; 150 } 151 152 int32_t CurrentTargetOffset() const { 153 return table_.GetEntryAt(index_ + first_target_offset_); 154 } 155 156 uint32_t GetDexPcForCurrentIndex() const { return table_.GetDexPcForIndex(index_); } 157 158 private: 159 const DexSwitchTable& table_; 160 const size_t num_entries_; 161 const size_t first_target_offset_; 162 163 size_t index_; 164 }; 165 166 inline const Instruction& GetDexInstructionAt(const DexFile::CodeItem& code_item, uint32_t dex_pc) { 167 return CodeItemIterator(code_item, dex_pc).CurrentInstruction(); 168 } 169 170 inline bool IsThrowingDexInstruction(const Instruction& instruction) { 171 // Special-case MONITOR_EXIT which is a throwing instruction but the verifier 172 // guarantees that it will never throw. This is necessary to avoid rejecting 173 // 'synchronized' blocks/methods. 174 return instruction.IsThrow() && instruction.Opcode() != Instruction::MONITOR_EXIT; 175 } 176 177 } // namespace art 178 179 #endif // ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_ 180