Binary search stack maps by native pc.

Test: test.py --host -r -b -t 018 -t 510
Change-Id: I07042e8dfd82adcd24fdfe1a1970a7ccdc09ce46
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index fb5d933..e8b3330 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -61,6 +61,16 @@
   current_stack_map_[StackMap::kPackedNativePc] =
       StackMap::PackNativePc(native_pc_offset, instruction_set_);
   current_stack_map_[StackMap::kDexPc] = dex_pc;
+  if (stack_maps_.size() > 0) {
+    // Check that non-catch stack maps are sorted by pc.
+    // Catch stack maps are at the end and may be unordered.
+    if (stack_maps_.back()[StackMap::kKind] == StackMap::Kind::Catch) {
+      DCHECK(current_stack_map_[StackMap::kKind] == StackMap::Kind::Catch);
+    } else if (current_stack_map_[StackMap::kKind] != StackMap::Kind::Catch) {
+      DCHECK_LE(stack_maps_.back()[StackMap::kPackedNativePc],
+                current_stack_map_[StackMap::kPackedNativePc]);
+    }
+  }
   if (register_mask != 0) {
     uint32_t shift = LeastSignificantBit(register_mask);
     BitTableBuilder<RegisterMask>::Entry entry;
diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h
index 053bf1f..8036db1 100644
--- a/libartbase/base/bit_table.h
+++ b/libartbase/base/bit_table.h
@@ -188,13 +188,55 @@
 template<typename Accessor>
 class BitTable : public BitTableBase<Accessor::kNumColumns> {
  public:
+  class const_iterator : public std::iterator<std::random_access_iterator_tag,
+                                              /* value_type */ Accessor,
+                                              /* difference_type */ int32_t,
+                                              /* pointer */ void,
+                                              /* reference */ void> {
+   public:
+    using difference_type = int32_t;
+    const_iterator() {}
+    const_iterator(const BitTable* table, uint32_t row) : table_(table), row_(row) {}
+    const_iterator operator+(difference_type n) { return const_iterator(table_, row_ + n); }
+    const_iterator operator-(difference_type n) { return const_iterator(table_, row_ - n); }
+    difference_type operator-(const const_iterator& other) { return row_ - other.row_; }
+    void operator+=(difference_type rows) { row_ += rows; }
+    void operator-=(difference_type rows) { row_ -= rows; }
+    const_iterator operator++() { return const_iterator(table_, ++row_); }
+    const_iterator operator--() { return const_iterator(table_, --row_); }
+    const_iterator operator++(int) { return const_iterator(table_, row_++); }
+    const_iterator operator--(int) { return const_iterator(table_, row_--); }
+    bool operator==(const_iterator i) const { DCHECK(table_ == i.table_); return row_ == i.row_; }
+    bool operator!=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ != i.row_; }
+    bool operator<=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ <= i.row_; }
+    bool operator>=(const_iterator i) const { DCHECK(table_ == i.table_); return row_ >= i.row_; }
+    bool operator<(const_iterator i) const { DCHECK(table_ == i.table_); return row_ < i.row_; }
+    bool operator>(const_iterator i) const { DCHECK(table_ == i.table_); return row_ > i.row_; }
+    Accessor operator*() { return Accessor(table_, row_); }
+    Accessor operator->() { return Accessor(table_, row_); }
+    Accessor operator[](size_t index) { return Accessor(table_, row_ + index); }
+   private:
+    const BitTable* table_ = nullptr;
+    uint32_t row_ = 0;
+  };
+
   using BitTableBase<Accessor::kNumColumns>::BitTableBase;  // Constructors.
 
+  ALWAYS_INLINE const_iterator begin() const { return const_iterator(this, 0); }
+  ALWAYS_INLINE const_iterator end() const { return const_iterator(this, this->NumRows()); }
+
   ALWAYS_INLINE Accessor GetRow(uint32_t row) const {
     return Accessor(this, row);
   }
 };
 
+template<typename Accessor>
+typename BitTable<Accessor>::const_iterator operator+(
+    typename BitTable<Accessor>::const_iterator::difference_type n,
+    typename BitTable<Accessor>::const_iterator a) {
+  return a + n;
+}
+
 // Helper class for encoding BitTable. It can optionally de-duplicate the inputs.
 template<uint32_t kNumColumns>
 class BitTableBuilderBase {
@@ -234,6 +276,7 @@
 
   Entry& operator[](size_t row) { return rows_[row]; }
   const Entry& operator[](size_t row) const { return rows_[row]; }
+  const Entry& back() const { return rows_.back(); }
   size_t size() const { return rows_.size(); }
 
   // Append given value to the vector without de-duplication.
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index f2418d0..f40168b 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -26,6 +26,27 @@
 
 namespace art {
 
+BitTable<StackMap>::const_iterator CodeInfo::BinarySearchNativePc(uint32_t packed_pc) const {
+  return std::partition_point(
+      stack_maps_.begin(),
+      stack_maps_.end(),
+      [packed_pc](const StackMap& sm) {
+        return sm.GetPackedNativePc() < packed_pc && sm.GetKind() != StackMap::Kind::Catch;
+      });
+}
+
+StackMap CodeInfo::GetStackMapForNativePcOffset(uint32_t pc, InstructionSet isa) const {
+  auto it = BinarySearchNativePc(StackMap::PackNativePc(pc, isa));
+  // Start at the lower bound and iterate over all stack maps with the given native pc.
+  for (; it != stack_maps_.end() && (*it).GetNativePcOffset(isa) == pc; ++it) {
+    StackMap::Kind kind = static_cast<StackMap::Kind>((*it).GetKind());
+    if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) {
+      return *it;
+    }
+  }
+  return StackMap();
+}
+
 // Scan backward to determine dex register locations at given stack map.
 // All registers for a stack map are combined - inlined registers are just appended,
 // therefore 'first_dex_register' allows us to select a sub-range to decode.
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 64a084f..7aac792 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -412,21 +412,7 @@
     return StackMap();
   }
 
-  StackMap GetStackMapForNativePcOffset(uint32_t pc, InstructionSet isa = kRuntimeISA) const {
-    // TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack
-    //       maps are not. If we knew that the method does not have try/catch,
-    //       we could do binary search.
-    for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
-      StackMap stack_map = GetStackMapAt(i);
-      if (stack_map.GetNativePcOffset(isa) == pc) {
-        StackMap::Kind kind = static_cast<StackMap::Kind>(stack_map.GetKind());
-        if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) {
-          return stack_map;
-        }
-      }
-    }
-    return StackMap();
-  }
+  StackMap GetStackMapForNativePcOffset(uint32_t pc, InstructionSet isa = kRuntimeISA) const;
 
   InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset) {
     for (size_t index = 0; index < invoke_infos_.NumRows(); index++) {
@@ -450,6 +436,10 @@
   void AddSizeStats(/*out*/ Stats* parent) const;
 
  private:
+  // Returns lower bound (fist stack map which has pc greater or equal than the desired one).
+  // It ignores catch stack maps at the end (it is the same as if they had maximum pc value).
+  BitTable<StackMap>::const_iterator BinarySearchNativePc(uint32_t packed_pc) const;
+
   // Scan backward to determine dex register locations at given stack map.
   void DecodeDexRegisterMap(uint32_t stack_map_index,
                             uint32_t first_dex_register,