Optimizing: Add debugging output for HInstruction.

Allow printing individual instruction and its arguments with
the HGraphVisualizer. Arguments are dumped "recursively"
(but implemented with a queue instead of actual recursion).

For example, printing the Return instruction from the method
Main.testLoop17 in 530-checker-lse yields

v28 Return [i27] dex_pc:23 loop:none
  i27 Add [i24,i26] dex_pc:22 loop:none
    i24 Phi [i5,i15] dex_pc:n/a reg:0 is_catch_phi:false loop:none
      i5 IntConstant dex_pc:0 1 loop:none
      i15 IntConstant dex_pc:5 2 loop:none
    i26 InstanceFieldGet [l6] dex_pc:20 field_name:TestClass.i field_type:Int32 loop:none
      l6 NullCheck [l1] dex_pc:1 env:[[i5,_,_,l1,i2]] loop:none
        l1 ParameterValue dex_pc:n/a loop:none

Test: Manual; modify LSE to print the instruction above.
Change-Id: Iaf41ba62cd6a5a36236ad0abca082ebffcf6a20e
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index e3e4589..d57eaf0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1312,6 +1312,44 @@
   orig_instr->FixUpUserRecordsAfterEnvUseRemoval(before_use_node);
 }
 
+std::ostream& HInstruction::Dump(std::ostream& os, bool dump_args) {
+  HGraph* graph = GetBlock()->GetGraph();
+  HGraphVisualizer::DumpInstruction(&os, graph, this);
+  if (dump_args) {
+    // Allocate memory from local ScopedArenaAllocator.
+    ScopedArenaAllocator allocator(graph->GetArenaStack());
+    // Instructions that we already visited. We print each instruction only once.
+    ArenaBitVector visited(
+        &allocator, graph->GetCurrentInstructionId(), /* expandable= */ false, kArenaAllocMisc);
+    visited.ClearAllBits();
+    visited.SetBit(GetId());
+    // Keep a queue of instructions with their indentations.
+    ScopedArenaDeque<std::pair<HInstruction*, size_t>> queue(allocator.Adapter(kArenaAllocMisc));
+    auto add_args = [&queue](HInstruction* instruction, size_t indentation) {
+      for (HInstruction* arg : ReverseRange(instruction->GetInputs())) {
+        queue.emplace_front(arg, indentation);
+      }
+    };
+    add_args(this, /*indentation=*/ 1u);
+    while (!queue.empty()) {
+      HInstruction* instruction;
+      size_t indentation;
+      std::tie(instruction, indentation) = queue.front();
+      queue.pop_front();
+      if (!visited.IsBitSet(instruction->GetId())) {
+        visited.SetBit(instruction->GetId());
+        os << '\n';
+        for (size_t i = 0; i != indentation; ++i) {
+          os << "  ";
+        }
+        HGraphVisualizer::DumpInstruction(&os, graph, instruction);
+        add_args(instruction, indentation + 1u);
+      }
+    }
+  }
+  return os;
+}
+
 HInstruction* HInstruction::GetNextDisregardingMoves() const {
   HInstruction* next = GetNext();
   while (next != nullptr && next->IsParallelMove()) {