Home | History | Annotate | Download | only in optimizing
      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