Add operator<< for HGraph and HInstructions.

Include helpers for printing arguments as well.

Test: ./test.py --host
Change-Id: I692fd5bd32a8a39da0defd9454d56ccf2480f229
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 523aab6..f4217eb 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -37,6 +37,7 @@
         "jni/quick/calling_convention.cc",
         "jni/quick/jni_compiler.cc",
         "optimizing/block_builder.cc",
+        "optimizing/block_namer.cc",
         "optimizing/bounds_check_elimination.cc",
         "optimizing/builder.cc",
         "optimizing/cha_guard_optimization.cc",
diff --git a/compiler/optimizing/block_namer.cc b/compiler/optimizing/block_namer.cc
new file mode 100644
index 0000000..9936bf1
--- /dev/null
+++ b/compiler/optimizing/block_namer.cc
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "block_namer.h"
+
+#include "nodes.h"
+
+namespace art {
+
+std::ostream& BlockNamer::PrintName(std::ostream& os, HBasicBlock* blk) const {
+  return os << "B" << blk->GetBlockId();
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/block_namer.h b/compiler/optimizing/block_namer.h
new file mode 100644
index 0000000..ed396b9
--- /dev/null
+++ b/compiler/optimizing/block_namer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_BLOCK_NAMER_H_
+#define ART_COMPILER_OPTIMIZING_BLOCK_NAMER_H_
+
+#include <ostream>
+
+namespace art {
+class HBasicBlock;
+
+struct BlockNamer {
+ public:
+  struct NameWrapper {
+    HBasicBlock* blk_;
+    const BlockNamer& namer_;
+  };
+  virtual ~BlockNamer() {}
+  virtual std::ostream& PrintName(std::ostream& os, HBasicBlock* blk) const;
+  NameWrapper GetName(HBasicBlock* blk) const {
+    return NameWrapper{blk, *this};
+  }
+};
+
+inline std::ostream& operator<<(std::ostream& os, const BlockNamer::NameWrapper& blk) {
+  return blk.namer_.PrintName(os, blk.blk_);
+}
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_BLOCK_NAMER_H_
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 44a64c1..da34af2 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -44,6 +44,10 @@
 
 namespace art {
 
+// Unique pass-name to identify that the dump is for printing to log.
+constexpr const char* kDebugDumpName = "debug";
+constexpr const char* kDebugDumpGraphName = "debug_graph";
+
 using android::base::StringPrintf;
 
 static bool HasWhitespace(const char* str) {
@@ -196,6 +200,7 @@
                           bool is_after_pass,
                           bool graph_in_bad_state,
                           const CodeGenerator* codegen,
+                          const BlockNamer& namer,
                           const DisassemblyInformation* disasm_info = nullptr)
       : HGraphDelegateVisitor(graph),
         output_(output),
@@ -204,6 +209,7 @@
         graph_in_bad_state_(graph_in_bad_state),
         codegen_(codegen),
         disasm_info_(disasm_info),
+        namer_(namer),
         disassembler_(disasm_info_ != nullptr
                       ? new HGraphVisualizerDisassembler(
                             codegen_->GetInstructionSet(),
@@ -231,6 +237,11 @@
     output_ << "end_" << name << "\n";
   }
 
+  void PrintProperty(const char* name, HBasicBlock* blk) {
+    AddIndent();
+    output_ << name << " \"" << namer_.GetName(blk) << "\"\n";
+  }
+
   void PrintProperty(const char* name, const char* property) {
     AddIndent();
     output_ << name << " \"" << property << "\"\n";
@@ -266,7 +277,7 @@
     AddIndent();
     output_ << "predecessors";
     for (HBasicBlock* predecessor : block->GetPredecessors()) {
-      output_ << " \"B" << predecessor->GetBlockId() << "\" ";
+      output_ << " \"" << namer_.GetName(predecessor) << "\" ";
     }
     if (block->IsEntryBlock() && (disasm_info_ != nullptr)) {
       output_ << " \"" << kDisassemblyBlockFrameEntry << "\" ";
@@ -278,20 +289,24 @@
     AddIndent();
     output_ << "successors";
     for (HBasicBlock* successor : block->GetNormalSuccessors()) {
-      output_ << " \"B" << successor->GetBlockId() << "\" ";
+      output_ << " \"" << namer_.GetName(successor) << "\" ";
     }
     output_<< "\n";
   }
 
   void PrintExceptionHandlers(HBasicBlock* block) {
+    bool has_slow_paths = block->IsExitBlock() &&
+                          (disasm_info_ != nullptr) &&
+                          !disasm_info_->GetSlowPathIntervals().empty();
+    if (IsDebugDump() && block->GetExceptionalSuccessors().empty() && !has_slow_paths) {
+      return;
+    }
     AddIndent();
     output_ << "xhandlers";
     for (HBasicBlock* handler : block->GetExceptionalSuccessors()) {
-      output_ << " \"B" << handler->GetBlockId() << "\" ";
+      output_ << " \"" << namer_.GetName(handler) << "\" ";
     }
-    if (block->IsExitBlock() &&
-        (disasm_info_ != nullptr) &&
-        !disasm_info_->GetSlowPathIntervals().empty()) {
+    if (has_slow_paths) {
       output_ << " \"" << kDisassemblyBlockSlowPaths << "\" ";
     }
     output_<< "\n";
@@ -393,11 +408,10 @@
 
   void VisitLoadClass(HLoadClass* load_class) override {
     StartAttributeStream("load_kind") << load_class->GetLoadKind();
-    const char* descriptor = load_class->GetDexFile().GetTypeDescriptor(
-        load_class->GetDexFile().GetTypeId(load_class->GetTypeIndex()));
-    StartAttributeStream("class_name") << PrettyDescriptor(descriptor);
-    StartAttributeStream("gen_clinit_check") << std::boolalpha
-        << load_class->MustGenerateClinitCheck() << std::noboolalpha;
+    StartAttributeStream("class_name")
+        << load_class->GetDexFile().PrettyType(load_class->GetTypeIndex());
+    StartAttributeStream("gen_clinit_check")
+        << std::boolalpha << load_class->MustGenerateClinitCheck() << std::noboolalpha;
     StartAttributeStream("needs_access_check") << std::boolalpha
         << load_class->NeedsAccessCheck() << std::noboolalpha;
   }
@@ -410,8 +424,13 @@
   void VisitLoadMethodType(HLoadMethodType* load_method_type) override {
     StartAttributeStream("load_kind") << "RuntimeCall";
     const DexFile& dex_file = load_method_type->GetDexFile();
-    const dex::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex());
-    StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id);
+    if (dex_file.NumProtoIds() >= load_method_type->GetProtoIndex().index_) {
+      const dex::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex());
+      StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id);
+    } else {
+      StartAttributeStream("method_type")
+          << "<<Unknown proto-idx: " << load_method_type->GetProtoIndex() << ">>";
+    }
   }
 
   void VisitLoadString(HLoadString* load_string) override {
@@ -609,6 +628,10 @@
     return strcmp(pass_name_, name) == 0;
   }
 
+  bool IsDebugDump() {
+    return IsPass(kDebugDumpGraphName) || IsPass(kDebugDumpName);
+  }
+
   void PrintInstruction(HInstruction* instruction) {
     output_ << instruction->DebugName();
     HConstInputsRef inputs = instruction->GetInputs();
@@ -624,6 +647,10 @@
     } else {
       StartAttributeStream("dex_pc") << "n/a";
     }
+    if (IsPass(kDebugDumpName)) {
+      // Include block name for logcat use.
+      StartAttributeStream("block") << namer_.GetName(instruction->GetBlock());
+    }
     instruction->Accept(this);
     if (instruction->HasEnvironment()) {
       StringList envs;
@@ -678,10 +705,10 @@
     if (loop_info == nullptr) {
       StartAttributeStream("loop") << "none";
     } else {
-      StartAttributeStream("loop") << "B" << loop_info->GetHeader()->GetBlockId();
+      StartAttributeStream("loop") << namer_.GetName(loop_info->GetHeader());
       HLoopInformation* outer = loop_info->GetPreHeader()->GetLoopInformation();
       if (outer != nullptr) {
-        StartAttributeStream("outer_loop") << "B" << outer->GetHeader()->GetBlockId();
+        StartAttributeStream("outer_loop") << namer_.GetName(outer->GetHeader());
       } else {
         StartAttributeStream("outer_loop") << "none";
       }
@@ -692,7 +719,8 @@
     // For the builder and the inliner, we want to add extra information on HInstructions
     // that have reference types, and also HInstanceOf/HCheckcast.
     if ((IsPass(HGraphBuilder::kBuilderPassName)
-        || IsPass(HInliner::kInlinerPassName))
+        || IsPass(HInliner::kInlinerPassName)
+        || IsDebugDump())
         && (instruction->GetType() == DataType::Type::kReference ||
             instruction->IsInstanceOf() ||
             instruction->IsCheckCast())) {
@@ -722,6 +750,7 @@
         // doesn't run or doesn't inline anything, the NullConstant remains untyped.
         // So we should check NullConstants for validity only after reference type propagation.
         DCHECK(graph_in_bad_state_ ||
+               IsDebugDump() ||
                (!is_after_pass_ && IsPass(HGraphBuilder::kBuilderPassName)))
             << instruction->DebugName() << instruction->GetId() << " has invalid rti "
             << (is_after_pass_ ? "after" : "before") << " pass " << pass_name_;
@@ -821,12 +850,13 @@
 
   void Run() {
     StartTag("cfg");
-    std::string pass_desc = std::string(pass_name_)
-                          + " ("
-                          + (is_after_pass_ ? "after" : "before")
-                          + (graph_in_bad_state_ ? ", bad_state" : "")
-                          + ")";
-    PrintProperty("name", pass_desc.c_str());
+    std::ostringstream oss;
+    oss << pass_name_;
+    if (!IsDebugDump()) {
+      oss << " (" << (is_after_pass_ ? "after" : "before")
+          << (graph_in_bad_state_ ? ", bad_state" : "") << ")";
+    }
+    PrintProperty("name", oss.str().c_str());
     if (disasm_info_ != nullptr) {
       DumpDisassemblyBlockForFrameEntry();
     }
@@ -846,12 +876,13 @@
 
   void VisitBasicBlock(HBasicBlock* block) override {
     StartTag("block");
-    PrintProperty("name", "B", block->GetBlockId());
+    PrintProperty("name", block);
     if (block->GetLifetimeStart() != kNoLifetime) {
       // Piggy back on these fields to show the lifetime of the block.
       PrintInt("from_bci", block->GetLifetimeStart());
       PrintInt("to_bci", block->GetLifetimeEnd());
-    } else {
+    } else if (!IsDebugDump()) {
+      // Don't print useless information to logcat.
       PrintInt("from_bci", -1);
       PrintInt("to_bci", -1);
     }
@@ -861,30 +892,33 @@
 
     if (block->IsCatchBlock()) {
       PrintProperty("flags", "catch_block");
-    } else {
+    } else if (!IsDebugDump()) {
+      // Don't print useless information to logcat
       PrintEmptyProperty("flags");
     }
 
     if (block->GetDominator() != nullptr) {
-      PrintProperty("dominator", "B", block->GetDominator()->GetBlockId());
+      PrintProperty("dominator", block->GetDominator());
     }
 
-    StartTag("states");
-    StartTag("locals");
-    PrintInt("size", 0);
-    PrintProperty("method", "None");
-    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-      AddIndent();
-      HInstruction* instruction = it.Current();
-      output_ << instruction->GetId() << " " << DataType::TypeId(instruction->GetType())
-              << instruction->GetId() << "[ ";
-      for (const HInstruction* input : instruction->GetInputs()) {
-        output_ << input->GetId() << " ";
+    if (!IsDebugDump() || !block->GetPhis().IsEmpty()) {
+      StartTag("states");
+      StartTag("locals");
+      PrintInt("size", 0);
+      PrintProperty("method", "None");
+      for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+        AddIndent();
+        HInstruction* instruction = it.Current();
+        output_ << instruction->GetId() << " " << DataType::TypeId(instruction->GetType())
+                << instruction->GetId() << "[ ";
+        for (const HInstruction* input : instruction->GetInputs()) {
+          output_ << input->GetId() << " ";
+        }
+        output_ << "]\n";
       }
-      output_ << "]\n";
+      EndTag("locals");
+      EndTag("states");
     }
-    EndTag("locals");
-    EndTag("states");
 
     StartTag("HIR");
     PrintInstructions(block->GetPhis());
@@ -904,20 +938,31 @@
   const bool graph_in_bad_state_;
   const CodeGenerator* codegen_;
   const DisassemblyInformation* disasm_info_;
+  const BlockNamer& namer_;
   std::unique_ptr<HGraphVisualizerDisassembler> disassembler_;
   size_t indent_;
 
   DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter);
 };
 
+std::ostream& HGraphVisualizer::OptionalDefaultNamer::PrintName(std::ostream& os,
+                                                                HBasicBlock* blk) const {
+  if (namer_) {
+    return namer_->get().PrintName(os, blk);
+  } else {
+    return BlockNamer::PrintName(os, blk);
+  }
+}
+
 HGraphVisualizer::HGraphVisualizer(std::ostream* output,
                                    HGraph* graph,
-                                   const CodeGenerator* codegen)
-  : output_(output), graph_(graph), codegen_(codegen) {}
+                                   const CodeGenerator* codegen,
+                                   std::optional<std::reference_wrapper<const BlockNamer>> namer)
+    : output_(output), graph_(graph), codegen_(codegen), namer_(namer) {}
 
 void HGraphVisualizer::PrintHeader(const char* method_name) const {
   DCHECK(output_ != nullptr);
-  HGraphVisualizerPrinter printer(graph_, *output_, "", true, false, codegen_);
+  HGraphVisualizerPrinter printer(graph_, *output_, "", true, false, codegen_, namer_);
   printer.StartTag("compilation");
   printer.PrintProperty("name", method_name);
   printer.PrintProperty("method", method_name);
@@ -939,6 +984,12 @@
                       time_str.c_str());
 }
 
+void HGraphVisualizer::DumpGraphDebug() const {
+  DumpGraph(/* pass_name= */ kDebugDumpGraphName,
+            /* is_after_pass= */ false,
+            /* graph_in_bad_state= */ true);
+}
+
 void HGraphVisualizer::DumpGraph(const char* pass_name,
                                  bool is_after_pass,
                                  bool graph_in_bad_state) const {
@@ -949,7 +1000,8 @@
                                     pass_name,
                                     is_after_pass,
                                     graph_in_bad_state,
-                                    codegen_);
+                                    codegen_,
+                                    namer_);
     printer.Run();
   }
 }
@@ -963,6 +1015,7 @@
                                     /* is_after_pass= */ true,
                                     /* graph_in_bad_state= */ false,
                                     codegen_,
+                                    namer_,
                                     codegen_->GetDisassemblyInformation());
     printer.Run();
   }
@@ -971,12 +1024,14 @@
 void HGraphVisualizer::DumpInstruction(std::ostream* output,
                                        HGraph* graph,
                                        HInstruction* instruction) {
+  BlockNamer namer;
   HGraphVisualizerPrinter printer(graph,
                                   *output,
-                                  /* pass_name= */ "debug",
+                                  /* pass_name= */ kDebugDumpName,
                                   /* is_after_pass= */ false,
                                   /* graph_in_bad_state= */ false,
-                                  /* codegen= */ nullptr);
+                                  /* codegen= */ nullptr,
+                                  /* namer= */ namer);
   printer.Run(instruction);
 }
 
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index b83e887..3429c11 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -17,11 +17,13 @@
 #ifndef ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_
 #define ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_
 
+#include <functional>
 #include <ostream>
 
 #include "arch/instruction_set.h"
 #include "base/arena_containers.h"
 #include "base/value_object.h"
+#include "block_namer.h"
 
 namespace art {
 
@@ -101,10 +103,12 @@
  public:
   HGraphVisualizer(std::ostream* output,
                    HGraph* graph,
-                   const CodeGenerator* codegen);
+                   const CodeGenerator* codegen,
+                   std::optional<std::reference_wrapper<const BlockNamer>> namer = std::nullopt);
 
   void PrintHeader(const char* method_name) const;
   void DumpGraph(const char* pass_name, bool is_after_pass, bool graph_in_bad_state) const;
+  void DumpGraphDebug() const;
   void DumpGraphWithDisassembly() const;
 
   // C1visualizer file format does not support inserting arbitrary metadata into a cfg
@@ -115,9 +119,21 @@
   static void DumpInstruction(std::ostream* output, HGraph* graph, HInstruction* instruction);
 
  private:
+  class OptionalDefaultNamer final : public BlockNamer {
+   public:
+    explicit OptionalDefaultNamer(std::optional<std::reference_wrapper<const BlockNamer>> inner)
+        : namer_(inner) {}
+
+    std::ostream& PrintName(std::ostream& os, HBasicBlock* blk) const override;
+
+   private:
+    std::optional<std::reference_wrapper<const BlockNamer>> namer_;
+  };
+
   std::ostream* const output_;
   HGraph* const graph_;
   const CodeGenerator* codegen_;
+  OptionalDefaultNamer namer_;
 
   DISALLOW_COPY_AND_ASSIGN(HGraphVisualizer);
 };
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index d57eaf0..e815474 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -17,6 +17,7 @@
 
 #include <algorithm>
 #include <cfloat>
+#include <functional>
 
 #include "art_method-inl.h"
 #include "base/arena_allocator.h"
@@ -1955,6 +1956,58 @@
   return os;
 }
 
+std::ostream& operator<<(std::ostream& os, const HInstruction::NoArgsDump rhs) {
+  // TODO Really this should be const but that would require const-ifying
+  // graph-visualizer and HGraphVisitor which are tangled up everywhere.
+  return const_cast<HInstruction*>(rhs.ins)->Dump(os, /* dump_args= */ false);
+}
+
+std::ostream& operator<<(std::ostream& os, const HInstruction::ArgsDump rhs) {
+  // TODO Really this should be const but that would require const-ifying
+  // graph-visualizer and HGraphVisitor which are tangled up everywhere.
+  return const_cast<HInstruction*>(rhs.ins)->Dump(os, /* dump_args= */ true);
+}
+
+std::ostream& operator<<(std::ostream& os, const HInstruction& rhs) {
+  return os << rhs.DumpWithoutArgs();
+}
+
+std::ostream& operator<<(std::ostream& os, const HUseList<HInstruction*>& lst) {
+  os << "Instructions[";
+  bool first = true;
+  for (const auto& hi : lst) {
+    if (!first) {
+      os << ", ";
+    }
+    first = false;
+    os << hi.GetUser()->DebugName() << "[id: " << hi.GetUser()->GetId()
+       << ", blk: " << hi.GetUser()->GetBlock()->GetBlockId() << "]@" << hi.GetIndex();
+  }
+  os << "]";
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const HUseList<HEnvironment*>& lst) {
+  os << "Environments[";
+  bool first = true;
+  for (const auto& hi : lst) {
+    if (!first) {
+      os << ", ";
+    }
+    first = false;
+    os << *hi.GetUser()->GetHolder() << "@" << hi.GetIndex();
+  }
+  os << "]";
+  return os;
+}
+
+std::ostream& HGraph::Dump(std::ostream& os,
+                           std::optional<std::reference_wrapper<const BlockNamer>> namer) {
+  HGraphVisualizer vis(&os, this, nullptr, namer);
+  vis.DumpGraphDebug();
+  return os;
+}
+
 void HInstruction::MoveBefore(HInstruction* cursor, bool do_checks) {
   if (do_checks) {
     DCHECK(!IsPhi());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 125f86b..73db7e5 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -33,6 +33,7 @@
 #include "base/stl_util.h"
 #include "base/transform_array_ref.h"
 #include "art_method.h"
+#include "block_namer.h"
 #include "class_root.h"
 #include "compilation_kind.h"
 #include "data_type.h"
@@ -423,6 +424,9 @@
     blocks_.reserve(kDefaultNumberOfBlocks);
   }
 
+  std::ostream& Dump(std::ostream& os,
+                     std::optional<std::reference_wrapper<const BlockNamer>> namer = std::nullopt);
+
   ArenaAllocator* GetAllocator() const { return allocator_; }
   ArenaStack* GetArenaStack() const { return arena_stack_; }
 
@@ -881,6 +885,10 @@
   DISALLOW_COPY_AND_ASSIGN(HGraph);
 };
 
+inline std::ostream& operator<<(std::ostream& os, HGraph& graph) {
+  return graph.Dump(os);
+}
+
 class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> {
  public:
   HLoopInformation(HBasicBlock* header, HGraph* graph)
@@ -2112,6 +2120,8 @@
   DISALLOW_COPY_AND_ASSIGN(HEnvironment);
 };
 
+std::ostream& operator<<(std::ostream& os, const HInstruction& rhs);
+
 class HInstruction : public ArenaObject<kArenaAllocInstruction> {
  public:
 #define DECLARE_KIND(type, super) k##type,
@@ -2147,6 +2157,21 @@
 
   std::ostream& Dump(std::ostream& os, bool dump_args = false);
 
+  // Helper for dumping without argument information using operator<<
+  struct NoArgsDump {
+    const HInstruction* ins;
+  };
+  NoArgsDump DumpWithoutArgs() const {
+    return NoArgsDump{this};
+  }
+  // Helper for dumping with argument information using operator<<
+  struct ArgsDump {
+    const HInstruction* ins;
+  };
+  ArgsDump DumpWithArgs() const {
+    return ArgsDump{this};
+  }
+
   HInstruction* GetNext() const { return next_; }
   HInstruction* GetPrevious() const { return previous_; }
 
@@ -2672,6 +2697,10 @@
   friend class HInstructionList;
 };
 std::ostream& operator<<(std::ostream& os, HInstruction::InstructionKind rhs);
+std::ostream& operator<<(std::ostream& os, const HInstruction::NoArgsDump rhs);
+std::ostream& operator<<(std::ostream& os, const HInstruction::ArgsDump rhs);
+std::ostream& operator<<(std::ostream& os, const HUseList<HInstruction*>& lst);
+std::ostream& operator<<(std::ostream& os, const HUseList<HEnvironment*>& lst);
 
 // Iterates over the instructions, while preserving the next instruction
 // in case the current instruction gets removed from the list by the user