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/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f9c63c4..44a64c1 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -195,7 +195,7 @@
                           const char* pass_name,
                           bool is_after_pass,
                           bool graph_in_bad_state,
-                          const CodeGenerator& codegen,
+                          const CodeGenerator* codegen,
                           const DisassemblyInformation* disasm_info = nullptr)
       : HGraphDelegateVisitor(graph),
         output_(output),
@@ -206,10 +206,10 @@
         disasm_info_(disasm_info),
         disassembler_(disasm_info_ != nullptr
                       ? new HGraphVisualizerDisassembler(
-                            codegen_.GetInstructionSet(),
-                            codegen_.GetAssembler().CodeBufferBaseAddress(),
-                            codegen_.GetAssembler().CodeBufferBaseAddress()
-                                + codegen_.GetAssembler().CodeSize())
+                            codegen_->GetInstructionSet(),
+                            codegen_->GetAssembler().CodeBufferBaseAddress(),
+                            codegen_->GetAssembler().CodeBufferBaseAddress()
+                                + codegen_->GetAssembler().CodeSize())
                       : nullptr),
         indent_(0) {}
 
@@ -298,10 +298,11 @@
   }
 
   void DumpLocation(std::ostream& stream, const Location& location) {
+    DCHECK(codegen_ != nullptr);
     if (location.IsRegister()) {
-      codegen_.DumpCoreRegister(stream, location.reg());
+      codegen_->DumpCoreRegister(stream, location.reg());
     } else if (location.IsFpuRegister()) {
-      codegen_.DumpFloatingPointRegister(stream, location.reg());
+      codegen_->DumpFloatingPointRegister(stream, location.reg());
     } else if (location.IsConstant()) {
       stream << "#";
       HConstant* constant = location.GetConstant();
@@ -321,13 +322,13 @@
     } else if (location.IsStackSlot()) {
       stream << location.GetStackIndex() << "(sp)";
     } else if (location.IsFpuRegisterPair()) {
-      codegen_.DumpFloatingPointRegister(stream, location.low());
+      codegen_->DumpFloatingPointRegister(stream, location.low());
       stream << "|";
-      codegen_.DumpFloatingPointRegister(stream, location.high());
+      codegen_->DumpFloatingPointRegister(stream, location.high());
     } else if (location.IsRegisterPair()) {
-      codegen_.DumpCoreRegister(stream, location.low());
+      codegen_->DumpCoreRegister(stream, location.low());
       stream << "|";
-      codegen_.DumpCoreRegister(stream, location.high());
+      codegen_->DumpCoreRegister(stream, location.high());
     } else if (location.IsUnallocated()) {
       stream << "unallocated";
     } else if (location.IsDoubleStackSlot()) {
@@ -837,6 +838,12 @@
     Flush();
   }
 
+  void Run(HInstruction* instruction) {
+    output_ << DataType::TypeId(instruction->GetType()) << instruction->GetId() << " ";
+    PrintInstruction(instruction);
+    Flush();
+  }
+
   void VisitBasicBlock(HBasicBlock* block) override {
     StartTag("block");
     PrintProperty("name", "B", block->GetBlockId());
@@ -895,7 +902,7 @@
   const char* pass_name_;
   const bool is_after_pass_;
   const bool graph_in_bad_state_;
-  const CodeGenerator& codegen_;
+  const CodeGenerator* codegen_;
   const DisassemblyInformation* disasm_info_;
   std::unique_ptr<HGraphVisualizerDisassembler> disassembler_;
   size_t indent_;
@@ -905,7 +912,7 @@
 
 HGraphVisualizer::HGraphVisualizer(std::ostream* output,
                                    HGraph* graph,
-                                   const CodeGenerator& codegen)
+                                   const CodeGenerator* codegen)
   : output_(output), graph_(graph), codegen_(codegen) {}
 
 void HGraphVisualizer::PrintHeader(const char* method_name) const {
@@ -956,9 +963,21 @@
                                     /* is_after_pass= */ true,
                                     /* graph_in_bad_state= */ false,
                                     codegen_,
-                                    codegen_.GetDisassemblyInformation());
+                                    codegen_->GetDisassemblyInformation());
     printer.Run();
   }
 }
 
+void HGraphVisualizer::DumpInstruction(std::ostream* output,
+                                       HGraph* graph,
+                                       HInstruction* instruction) {
+  HGraphVisualizerPrinter printer(graph,
+                                  *output,
+                                  /* pass_name= */ "debug",
+                                  /* is_after_pass= */ false,
+                                  /* graph_in_bad_state= */ false,
+                                  /* codegen= */ nullptr);
+  printer.Run(instruction);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index e01d03c..b83e887 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -101,7 +101,7 @@
  public:
   HGraphVisualizer(std::ostream* output,
                    HGraph* graph,
-                   const CodeGenerator& codegen);
+                   const CodeGenerator* codegen);
 
   void PrintHeader(const char* method_name) const;
   void DumpGraph(const char* pass_name, bool is_after_pass, bool graph_in_bad_state) const;
@@ -112,10 +112,12 @@
   // method attributes is used. Such empty blocks don't break the c1visualizer parser.
   static std::string InsertMetaDataAsCompilationBlock(const std::string& meta_data);
 
+  static void DumpInstruction(std::ostream* output, HGraph* graph, HInstruction* instruction);
+
  private:
   std::ostream* const output_;
   HGraph* const graph_;
-  const CodeGenerator& codegen_;
+  const CodeGenerator* codegen_;
 
   DISALLOW_COPY_AND_ASSIGN(HGraphVisualizer);
 };
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()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index d15e40a..125f86b 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2145,6 +2145,7 @@
 
   virtual ~HInstruction() {}
 
+  std::ostream& Dump(std::ostream& os, bool dump_args = false);
 
   HInstruction* GetNext() const { return next_; }
   HInstruction* GetPrevious() const { return previous_; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b7e9d63..ac241aa 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -117,7 +117,7 @@
         visualizer_oss_(),
         visualizer_output_(visualizer_output),
         visualizer_enabled_(!compiler_options.GetDumpCfgFileName().empty()),
-        visualizer_(&visualizer_oss_, graph, *codegen),
+        visualizer_(&visualizer_oss_, graph, codegen),
         codegen_(codegen),
         visualizer_dump_mutex_(dump_mutex),
         graph_in_bad_state_(false) {