ART: Refactor try/catch block info, store exception type

This patch replaces HBasicBlock fields storing try/catch info with a
single TryCatchInformation data structure, saving memory for the
majority of non-try/catch blocks. It also changes builder to store
the exception type for catch blocks.

Change-Id: Ib3e43f7db247e6915d67c267fc62410420e230c9
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 8841498..c4ead5b 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -306,7 +306,8 @@
     for (; iterator.HasNext(); iterator.Next()) {
       uint32_t address = iterator.GetHandlerAddress();
       HBasicBlock* block = FindOrCreateBlockStartingAt(address);
-      block->SetIsCatchBlock();
+      block->SetTryCatchInformation(
+        new (arena_) TryCatchInformation(iterator.GetHandlerTypeIndex(), *dex_file_));
     }
     handlers_ptr = iterator.EndDataPointer();
   }
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 5406a0c..847d5a4 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -402,14 +402,14 @@
   }
 
   // Ensure try membership information is consistent.
-  HTryBoundary* try_entry = block->GetTryEntry();
   if (block->IsCatchBlock()) {
-    if (try_entry != nullptr) {
+    if (block->IsTryBlock()) {
+      const HTryBoundary& try_entry = block->GetTryCatchInformation()->GetTryEntry();
       AddError(StringPrintf("Catch blocks should not be try blocks but catch block %d "
                             "has try entry %s:%d.",
                             block->GetBlockId(),
-                            try_entry->DebugName(),
-                            try_entry->GetId()));
+                            try_entry.DebugName(),
+                            try_entry.GetId()));
     }
 
     if (block->IsLoopHeader()) {
@@ -419,29 +419,30 @@
   } else {
     for (size_t i = 0; i < block->GetPredecessors().Size(); ++i) {
       HBasicBlock* predecessor = block->GetPredecessors().Get(i);
-      HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors();
-      if (try_entry == nullptr) {
-        if (incoming_try_entry != nullptr) {
-          AddError(StringPrintf("Block %d has no try entry but try entry %s:%d follows "
+      const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors();
+      if (block->IsTryBlock()) {
+        const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry();
+        if (incoming_try_entry == nullptr) {
+          AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows "
                                 "from predecessor %d.",
                                 block->GetBlockId(),
+                                stored_try_entry.DebugName(),
+                                stored_try_entry.GetId(),
+                                predecessor->GetBlockId()));
+        } else if (!incoming_try_entry->HasSameExceptionHandlersAs(stored_try_entry)) {
+          AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent "
+                                "with %s:%d that follows from predecessor %d.",
+                                block->GetBlockId(),
+                                stored_try_entry.DebugName(),
+                                stored_try_entry.GetId(),
                                 incoming_try_entry->DebugName(),
                                 incoming_try_entry->GetId(),
                                 predecessor->GetBlockId()));
         }
-      } else if (incoming_try_entry == nullptr) {
-        AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows "
+      } else if (incoming_try_entry != nullptr) {
+        AddError(StringPrintf("Block %d is not a try block but try entry %s:%d follows "
                               "from predecessor %d.",
                               block->GetBlockId(),
-                              try_entry->DebugName(),
-                              try_entry->GetId(),
-                              predecessor->GetBlockId()));
-      } else if (!incoming_try_entry->HasSameExceptionHandlersAs(*try_entry)) {
-        AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent "
-                              "with %s:%d that follows from predecessor %d.",
-                              block->GetBlockId(),
-                              try_entry->DebugName(),
-                              try_entry->GetId(),
                               incoming_try_entry->DebugName(),
                               incoming_try_entry->GetId(),
                               predecessor->GetBlockId()));
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index f2b63ae..64c680c 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -339,7 +339,10 @@
     // been visited already and had its try membership set.
     HBasicBlock* first_predecessor = block->GetPredecessors().Get(0);
     DCHECK(!block->IsLoopHeader() || !block->GetLoopInformation()->IsBackEdge(*first_predecessor));
-    block->SetTryEntry(first_predecessor->ComputeTryEntryOfSuccessors());
+    const HTryBoundary* try_entry = first_predecessor->ComputeTryEntryOfSuccessors();
+    if (try_entry != nullptr) {
+      block->SetTryCatchInformation(new (arena_) TryCatchInformation(*try_entry));
+    }
   }
 }
 
@@ -1164,19 +1167,21 @@
   return new_block;
 }
 
-HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const {
+const HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const {
   if (EndsWithTryBoundary()) {
     HTryBoundary* try_boundary = GetLastInstruction()->AsTryBoundary();
     if (try_boundary->IsEntry()) {
-      DCHECK(try_entry_ == nullptr);
+      DCHECK(!IsTryBlock());
       return try_boundary;
     } else {
-      DCHECK(try_entry_ != nullptr);
-      DCHECK(try_entry_->HasSameExceptionHandlersAs(*try_boundary));
+      DCHECK(IsTryBlock());
+      DCHECK(try_catch_information_->GetTryEntry().HasSameExceptionHandlersAs(*try_boundary));
       return nullptr;
     }
+  } else if (IsTryBlock()) {
+    return &try_catch_information_->GetTryEntry();
   } else {
-    return try_entry_;
+    return nullptr;
   }
 }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f09e958..c4f64b4 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -555,6 +555,59 @@
   DISALLOW_COPY_AND_ASSIGN(HLoopInformation);
 };
 
+// Stores try/catch information for basic blocks.
+// Note that HGraph is constructed so that catch blocks cannot simultaneously
+// be try blocks.
+class TryCatchInformation : public ArenaObject<kArenaAllocMisc> {
+ public:
+  // Try block information constructor.
+  explicit TryCatchInformation(const HTryBoundary& try_entry)
+      : try_entry_(&try_entry),
+        catch_dex_file_(nullptr),
+        catch_type_index_(DexFile::kDexNoIndex16) {
+    DCHECK(try_entry_ != nullptr);
+  }
+
+  // Catch block information constructor.
+  TryCatchInformation(uint16_t catch_type_index, const DexFile& dex_file)
+      : try_entry_(nullptr),
+        catch_dex_file_(&dex_file),
+        catch_type_index_(catch_type_index) {}
+
+  bool IsTryBlock() const { return try_entry_ != nullptr; }
+
+  const HTryBoundary& GetTryEntry() const {
+    DCHECK(IsTryBlock());
+    return *try_entry_;
+  }
+
+  bool IsCatchBlock() const { return catch_dex_file_ != nullptr; }
+
+  bool IsCatchAllTypeIndex() const {
+    DCHECK(IsCatchBlock());
+    return catch_type_index_ == DexFile::kDexNoIndex16;
+  }
+
+  uint16_t GetCatchTypeIndex() const {
+    DCHECK(IsCatchBlock());
+    return catch_type_index_;
+  }
+
+  const DexFile& GetCatchDexFile() const {
+    DCHECK(IsCatchBlock());
+    return *catch_dex_file_;
+  }
+
+ private:
+  // One of possibly several TryBoundary instructions entering the block's try.
+  // Only set for try blocks.
+  const HTryBoundary* try_entry_;
+
+  // Exception type information. Only set for catch blocks.
+  const DexFile* catch_dex_file_;
+  const uint16_t catch_type_index_;
+};
+
 static constexpr size_t kNoLifetime = -1;
 static constexpr uint32_t kNoDexPc = -1;
 
@@ -575,7 +628,7 @@
         dex_pc_(dex_pc),
         lifetime_start_(kNoLifetime),
         lifetime_end_(kNoLifetime),
-        is_catch_block_(false) {}
+        try_catch_information_(nullptr) {}
 
   const GrowableArray<HBasicBlock*>& GetPredecessors() const {
     return predecessors_;
@@ -853,14 +906,24 @@
 
   bool IsInLoop() const { return loop_information_ != nullptr; }
 
-  HTryBoundary* GetTryEntry() const { return try_entry_; }
-  void SetTryEntry(HTryBoundary* try_entry) { try_entry_ = try_entry; }
-  bool IsInTry() const { return try_entry_ != nullptr; }
+  TryCatchInformation* GetTryCatchInformation() const { return try_catch_information_; }
+
+  void SetTryCatchInformation(TryCatchInformation* try_catch_information) {
+    try_catch_information_ = try_catch_information;
+  }
+
+  bool IsTryBlock() const {
+    return try_catch_information_ != nullptr && try_catch_information_->IsTryBlock();
+  }
+
+  bool IsCatchBlock() const {
+    return try_catch_information_ != nullptr && try_catch_information_->IsCatchBlock();
+  }
 
   // Returns the try entry that this block's successors should have. They will
   // be in the same try, unless the block ends in a try boundary. In that case,
   // the appropriate try entry will be returned.
-  HTryBoundary* ComputeTryEntryOfSuccessors() const;
+  const HTryBoundary* ComputeTryEntryOfSuccessors() const;
 
   // Returns whether this block dominates the blocked passed as parameter.
   bool Dominates(HBasicBlock* block) const;
@@ -873,9 +936,6 @@
 
   uint32_t GetDexPc() const { return dex_pc_; }
 
-  bool IsCatchBlock() const { return is_catch_block_; }
-  void SetIsCatchBlock() { is_catch_block_ = true; }
-
   bool EndsWithControlFlowInstruction() const;
   bool EndsWithIf() const;
   bool EndsWithTryBoundary() const;
@@ -895,11 +955,7 @@
   const uint32_t dex_pc_;
   size_t lifetime_start_;
   size_t lifetime_end_;
-  bool is_catch_block_;
-
-  // If this block is in a try block, `try_entry_` stores one of, possibly
-  // several, TryBoundary instructions entering it.
-  HTryBoundary* try_entry_;
+  TryCatchInformation* try_catch_information_;
 
   friend class HGraph;
   friend class HInstruction;
@@ -1676,7 +1732,9 @@
     UNREACHABLE();
   }
   virtual bool IsControlFlow() const { return false; }
+
   virtual bool CanThrow() const { return false; }
+  bool CanThrowIntoCatchBlock() const { return CanThrow() && block_->IsTryBlock(); }
 
   bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
   bool DoesAnyWrite() const { return side_effects_.DoesAnyWrite(); }
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index ff2e6ad..561c3b4 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -567,9 +567,10 @@
   }
 
   // If in a try block, propagate values of locals into catch blocks.
-  if (instruction->GetBlock()->IsInTry() && instruction->CanThrow()) {
-    HTryBoundary* try_block = instruction->GetBlock()->GetTryEntry();
-    for (HExceptionHandlerIterator it(*try_block); !it.Done(); it.Advance()) {
+  if (instruction->CanThrowIntoCatchBlock()) {
+    const HTryBoundary& try_entry =
+        instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
+    for (HExceptionHandlerIterator it(try_entry); !it.Done(); it.Advance()) {
       GrowableArray<HInstruction*>* handler_locals = GetLocalsFor(it.Current());
       for (size_t i = 0, e = current_locals_->Size(); i < e; ++i) {
         HInstruction* local_value = current_locals_->Get(i);