ART: Changes to try-catch in GraphBuilder

This patch adds an additional case into the insertion algorithm for
HTryBoundary inside HGraphBuilder in order to better handle catch
blocks covered by a TryItem.

Building SSA form also required to stop combining HTryBoundaries for
neighbouring TryItems because it was not clear which exception
handlers belong to which try block.

Change-Id: Ic68bd6ef98fee784609fa593cb08dca1f00a15e0
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index bde2c70..883c983 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -306,6 +306,22 @@
   }
 }
 
+void HGraphBuilder::SplitTryBoundaryEdge(HBasicBlock* predecessor,
+                                         HBasicBlock* successor,
+                                         HTryBoundary::BoundaryKind kind,
+                                         const DexFile::CodeItem& code_item,
+                                         const DexFile::TryItem& try_item) {
+  // Split the edge with a single TryBoundary instruction.
+  HTryBoundary* try_boundary = new (arena_) HTryBoundary(kind);
+  HBasicBlock* try_entry_block = graph_->SplitEdge(predecessor, successor);
+  try_entry_block->AddInstruction(try_boundary);
+
+  // Link the TryBoundary to the handlers of `try_item`.
+  for (CatchHandlerIterator it(code_item, try_item); it.HasNext(); it.Next()) {
+    try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress()));
+  }
+}
+
 void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) {
   if (code_item.tries_size_ == 0) {
     return;
@@ -326,47 +342,46 @@
         continue;
       }
 
-      // Find predecessors which are not covered by the same TryItem range. Such
-      // edges enter the try block and will have a TryBoundary inserted.
-      for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
-        HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
-        HTryBoundary* try_boundary = nullptr;
-        if (predecessor->IsSingleTryBoundary()) {
-          try_boundary = predecessor->GetLastInstruction()->AsTryBoundary();
-          if (try_boundary->GetNormalFlowSuccessor() == try_block
-              && try_block->IsFirstIndexOfPredecessor(predecessor, i)) {
+      if (try_block->IsCatchBlock()) {
+        // Catch blocks are always considered an entry point into the TryItem in
+        // order to avoid splitting exceptional edges (they might not have been
+        // created yet). We separate the move-exception (if present) from the
+        // rest of the block and insert a TryBoundary after it.
+        HInstruction* split_position = try_block->GetFirstInstruction();
+        if (split_position->IsLoadException()) {
+          DCHECK(split_position->GetNext()->IsStoreLocal());
+          split_position = split_position->GetNext()->GetNext();
+        }
+        DCHECK(split_position != nullptr);
+        HBasicBlock* catch_block = try_block;
+        try_block = catch_block->SplitBefore(split_position);
+        SplitTryBoundaryEdge(catch_block, try_block, HTryBoundary::kEntry, code_item, *try_item);
+      } else {
+        // For non-catch blocks, find predecessors which are not covered by the
+        // same TryItem range. Such edges enter the try block and will have
+        // a TryBoundary inserted.
+        for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
+          HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
+          if (predecessor->IsSingleTryBoundary()) {
             // The edge was already split because of an exit from a neighbouring
-            // TryItem and `predecessor` is the block with a TryBoundary created
-            // between the two original blocks. We do not split the edge again.
-            DCHECK(!IsBlockInPcRange(predecessor->GetSinglePredecessor(), try_start, try_end));
-            DCHECK(try_boundary->IsTryExit());
-            DCHECK(!try_boundary->IsTryEntry());
-            try_boundary->SetIsTryEntry();
+            // TryItem. We split it again and insert an entry point.
+            if (kIsDebugBuild) {
+              HTryBoundary* last_insn = predecessor->GetLastInstruction()->AsTryBoundary();
+              DCHECK(!last_insn->IsEntry());
+              DCHECK_EQ(last_insn->GetNormalFlowSuccessor(), try_block);
+              DCHECK(try_block->IsFirstIndexOfPredecessor(predecessor, i));
+              DCHECK(!IsBlockInPcRange(predecessor->GetSinglePredecessor(), try_start, try_end));
+            }
+          } else if (!IsBlockInPcRange(predecessor, try_start, try_end)) {
+            // This is an entry point into the TryItem and the edge has not been
+            // split yet. That means that `predecessor` is not in a TryItem, or
+            // it is in a different TryItem and we happened to iterate over this
+            // block first. We split the edge and insert an entry point.
           } else {
-            // This is an edge between a previously created TryBoundary and its
-            // handler. We skip it because it is exceptional flow.
-            DCHECK(try_block->IsCatchBlock());
-            DCHECK(try_boundary->HasExceptionHandler(try_block));
+            // Not an edge on the boundary of the try block.
             continue;
           }
-        } else if (!IsBlockInPcRange(predecessor, try_start, try_end)) {
-          // This is an entry point into the TryItem and the edge has not been
-          // split yet. That means that either `predecessor` is not in a TryItem,
-          // or it is in a different TryItem and we happened to iterate over
-          // this block first. We split the edge and `predecessor` may add its
-          // own exception handlers later.
-          try_boundary = new (arena_) HTryBoundary(/* is_entry */ true, /* is_exit */ false);
-          HBasicBlock* try_entry_block = graph_->SplitEdge(predecessor, try_block);
-          try_entry_block->AddInstruction(try_boundary);
-        } else {
-          // Not an edge on the boundary of the try block.
-          continue;
-        }
-        DCHECK(try_boundary != nullptr);
-
-        // Link the TryBoundary block to the handlers of this TryItem.
-        for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) {
-          try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress()));
+          SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, *try_item);
         }
       }
 
@@ -374,45 +389,37 @@
       // edges exit the try block and will have a TryBoundary inserted.
       for (size_t i = 0; i < try_block->GetSuccessors().Size(); ++i) {
         HBasicBlock* successor = try_block->GetSuccessors().Get(i);
-        HTryBoundary* try_boundary = nullptr;
-        if (successor->IsSingleTryBoundary()) {
+        if (successor->IsCatchBlock()) {
+          // A catch block is always considered an entry point into its TryItem.
+          // We therefore assume this is an exit point, regardless of whether
+          // the catch block is in a different TryItem or not.
+        } else if (successor->IsSingleTryBoundary()) {
           // The edge was already split because of an entry into a neighbouring
-          // TryItem. We do not split the edge again.
-          try_boundary = successor->GetLastInstruction()->AsTryBoundary();
-          DCHECK_EQ(try_block, successor->GetSinglePredecessor());
-          DCHECK(try_boundary->IsTryEntry());
-          DCHECK(!try_boundary->IsTryExit());
-          DCHECK(!IsBlockInPcRange(try_boundary->GetNormalFlowSuccessor(), try_start, try_end));
-          try_boundary->SetIsTryExit();
+          // TryItem. We split it again and insert an exit.
+          if (kIsDebugBuild) {
+            HTryBoundary* last_insn = successor->GetLastInstruction()->AsTryBoundary();
+            DCHECK_EQ(try_block, successor->GetSinglePredecessor());
+            DCHECK(last_insn->IsEntry());
+            DCHECK(!IsBlockInPcRange(last_insn->GetNormalFlowSuccessor(), try_start, try_end));
+          }
         } else if (!IsBlockInPcRange(successor, try_start, try_end)) {
           // This is an exit out of the TryItem and the edge has not been split
           // yet. That means that either `successor` is not in a TryItem, or it
           // is in a different TryItem and we happened to iterate over this
-          // block first. We split the edge and `successor` may add its own
-          // exception handlers later.
+          // block first. We split the edge and insert an exit.
           HInstruction* last_instruction = try_block->GetLastInstruction();
           if (last_instruction->IsReturn() || last_instruction->IsReturnVoid()) {
             DCHECK_EQ(successor, exit_block_);
             // Control flow exits the try block with a Return(Void). Because
             // splitting the edge would invalidate the invariant that Return
             // always jumps to Exit, we move the Return outside the try block.
-            HBasicBlock* return_block = try_block->SplitBefore(last_instruction);
-            graph_->AddBlock(return_block);
-            successor = return_block;
+            successor = try_block->SplitBefore(last_instruction);
           }
-          try_boundary = new (arena_) HTryBoundary(/* is_entry */ false, /* is_exit */ true);
-          HBasicBlock* try_exit_block = graph_->SplitEdge(try_block, successor);
-          try_exit_block->AddInstruction(try_boundary);
         } else {
           // Not an edge on the boundary of the try block.
           continue;
         }
-        DCHECK(try_boundary != nullptr);
-
-        // Link the TryBoundary block to the handlers of this TryItem.
-        for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) {
-          try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress()));
-        }
+        SplitTryBoundaryEdge(try_block, successor, HTryBoundary::kExit, code_item, *try_item);
       }
     }
   }
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 58d85e9..9744a55 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -97,9 +97,15 @@
   void MaybeUpdateCurrentBlock(size_t dex_pc);
   HBasicBlock* FindBlockStartingAt(int32_t dex_pc) const;
   HBasicBlock* FindOrCreateBlockStartingAt(int32_t dex_pc);
+
   bool IsBlockInPcRange(HBasicBlock* block, uint32_t dex_pc_start, uint32_t dex_pc_end);
   void CreateBlocksForTryCatch(const DexFile::CodeItem& code_item);
   void InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item);
+  void SplitTryBoundaryEdge(HBasicBlock* predecessor,
+                            HBasicBlock* successor,
+                            HTryBoundary::BoundaryKind kind,
+                            const DexFile::CodeItem& code_item,
+                            const DexFile::TryItem& try_item);
 
   void InitializeLocals(uint16_t count);
   HLocal* GetLocalAt(int register_index) const;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index c41574c..5cb9772 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -380,12 +380,7 @@
   }
 
   void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
-    StartAttributeStream("is_entry") << std::boolalpha
-                                     << try_boundary->IsTryEntry()
-                                     << std::noboolalpha;
-    StartAttributeStream("is_exit") << std::boolalpha
-                                    << try_boundary->IsTryExit()
-                                    << std::noboolalpha;
+    StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
   }
 
   bool IsPass(const char* name) {
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 881f9ec..b82e37c 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1050,6 +1050,7 @@
   successors_.Reset();
   AddSuccessor(new_block);
 
+  GetGraph()->AddBlock(new_block);
   return new_block;
 }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 95ea966..5c10245 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -729,9 +729,10 @@
   bool IsExceptionalSuccessor(size_t idx) const;
 
   // Split the block into two blocks just before `cursor`. Returns the newly
-  // created, latter block. Note that this method will create a Goto at the end
-  // of the former block and will create an edge between them. It will not,
-  // however, update the graph, reverse post order or loop information.
+  // created, latter block. Note that this method will add the block to the
+  // graph, create a Goto at the end of the former block and will create an edge
+  // between the blocks. It will not, however, update the reverse post order or
+  // loop information.
   HBasicBlock* SplitBefore(HInstruction* cursor);
 
   // Split the block into two blocks just after `cursor`. Returns the newly
@@ -1946,11 +1947,18 @@
 // higher indices in no particular order.
 class HTryBoundary : public HTemplateInstruction<0> {
  public:
-  HTryBoundary(bool is_entry, bool is_exit)
-      : HTemplateInstruction(SideEffects::None()), is_entry_(is_entry), is_exit_(is_exit) {}
+  enum BoundaryKind {
+    kEntry,
+    kExit,
+  };
+
+  explicit HTryBoundary(BoundaryKind kind)
+      : HTemplateInstruction(SideEffects::None()), kind_(kind) {}
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
+  bool CanThrow() const OVERRIDE { return true; }
+
   // Returns the block's non-exceptional successor (index zero).
   HBasicBlock* GetNormalFlowSuccessor() const { return GetBlock()->GetSuccessors().Get(0); }
 
@@ -1977,21 +1985,12 @@
     }
   }
 
-  bool IsTryEntry() const { return is_entry_; }
-  bool IsTryExit() const { return is_exit_; }
+  bool IsEntry() const { return kind_ == BoundaryKind::kEntry; }
 
   DECLARE_INSTRUCTION(TryBoundary);
 
  private:
-  // Only for debugging purposes.
-  bool is_entry_;
-  bool is_exit_;
-
-  // Only set by HGraphBuilder.
-  void SetIsTryEntry() { is_entry_ = true; }
-  void SetIsTryExit() { is_exit_ = true; }
-
-  friend HGraphBuilder;
+  const BoundaryKind kind_;
 
   DISALLOW_COPY_AND_ASSIGN(HTryBoundary);
 };