Adding ostream operators for IR structures

* Added for Instruction, BasicBlock, Function and Module
* Uses new disassembly functionality that can disassemble individual
instructions
 * For debug use only (no caching is done)
 * Each output converts module to binary, parses and outputs an
 individual instruction
* Added a test for whole module output
* Disabling Microsoft checked iterator warnings
* Updated check_copyright.py to accept 2018
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6feb6f..e76a663 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,7 +85,7 @@
     set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror)
   endif()
 elseif(MSVC)
-  set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS /wd4800)
+  set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800)
 
   if(${SPIRV_WERROR})
     set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX)
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index a306730..61a4112 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -228,6 +228,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/binary.h
   ${CMAKE_CURRENT_SOURCE_DIR}/cfa.h
   ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h
   ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h
   ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h
   ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index 5230a50..909886c 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -25,6 +25,7 @@
 #include "assembly_grammar.h"
 #include "binary.h"
 #include "diagnostic.h"
+#include "disassemble.h"
 #include "ext_inst.h"
 #include "name_mapper.h"
 #include "opcode.h"
@@ -352,6 +353,52 @@
   return disassembler->HandleInstruction(*parsed_instruction);
 }
 
+// Simple wrapper class to provide extra data necessary for targeted
+// instruction disassembly.
+class WrappedDisassembler {
+ public:
+  WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc)
+      : disassembler_(dis), inst_binary_(binary), word_count_(wc) {}
+
+  Disassembler* disassembler() { return disassembler_; }
+  const uint32_t* inst_binary() const { return inst_binary_; }
+  size_t word_count() const { return word_count_; }
+
+ private:
+  Disassembler* disassembler_;
+  const uint32_t* inst_binary_;
+  const size_t word_count_;
+};
+
+spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian,
+                                     uint32_t /* magic */, uint32_t version,
+                                     uint32_t generator, uint32_t id_bound,
+                                     uint32_t schema) {
+  assert(user_data);
+  auto wrapped = static_cast<WrappedDisassembler*>(user_data);
+  return wrapped->disassembler()->HandleHeader(endian, version, generator,
+                                               id_bound, schema);
+}
+
+spv_result_t DisassembleTargetInstruction(
+    void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
+  assert(user_data);
+  auto wrapped = static_cast<WrappedDisassembler*>(user_data);
+  // Check if this is the instruction we want to disassemble.
+  if (wrapped->word_count() == parsed_instruction->num_words &&
+      std::equal(wrapped->inst_binary(),
+                 wrapped->inst_binary() + wrapped->word_count(),
+                 parsed_instruction->words)) {
+    // Found the target instruction. Disassemble it and signal that we should
+    // stop searching so we don't output the same instruction again.
+    if (auto error =
+            wrapped->disassembler()->HandleInstruction(*parsed_instruction))
+      return error;
+    return SPV_REQUESTED_TERMINATION;
+  }
+  return SPV_SUCCESS;
+}
+
 }  // anonymous namespace
 
 spv_result_t spvBinaryToText(const spv_const_context context,
@@ -386,3 +433,44 @@
 
   return disassembler.SaveTextResult(pText);
 }
+
+std::string spvtools::spvInstructionBinaryToText(const spv_target_env env,
+                                                 const uint32_t* instCode,
+                                                 const size_t instWordCount,
+                                                 const uint32_t* code,
+                                                 const size_t wordCount,
+                                                 const uint32_t options) {
+  spv_context context = spvContextCreate(env);
+  const libspirv::AssemblyGrammar grammar(context);
+  if (!grammar.isValid()) {
+    spvContextDestroy(context);
+    return "";
+  }
+
+  // Generate friendly names for Ids if requested.
+  std::unique_ptr<libspirv::FriendlyNameMapper> friendly_mapper;
+  libspirv::NameMapper name_mapper = libspirv::GetTrivialNameMapper();
+  if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
+    friendly_mapper.reset(
+        new libspirv::FriendlyNameMapper(context, code, wordCount));
+    name_mapper = friendly_mapper->GetNameMapper();
+  }
+
+  // Now disassemble!
+  Disassembler disassembler(grammar, options, name_mapper);
+  WrappedDisassembler wrapped(&disassembler, instCode, instWordCount);
+  spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader,
+                 DisassembleTargetInstruction, nullptr);
+
+  spv_text text = nullptr;
+  std::string output;
+  if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) {
+    output.assign(text->str, text->str + text->length);
+    // Drop trailing newline characters.
+    while (!output.empty() && output.back() == '\n') output.pop_back();
+  }
+  spvTextDestroy(text);
+  spvContextDestroy(context);
+
+  return output;
+}
diff --git a/source/disassemble.h b/source/disassemble.h
new file mode 100644
index 0000000..b833dd0
--- /dev/null
+++ b/source/disassemble.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2018 Google Inc.
+//
+// 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 SPIRV_TOOLS_DISASSEMBLE_H_
+#define SPIRV_TOOLS_DISASSEMBLE_H_
+
+#include <string>
+
+#include "spirv-tools/libspirv.h"
+
+namespace spvtools {
+
+// Decodes the given SPIR-V instruction binary representation to its assembly
+// text. The context is inferred from the provided module binary. The options
+// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will
+// be stored into *text. Any error will be written into *diagnostic if
+// diagnostic is non-null.
+std::string spvInstructionBinaryToText(const spv_target_env env,
+                                       const uint32_t* inst_binary,
+                                       const size_t inst_word_count,
+                                       const uint32_t* binary,
+                                       const size_t word_count,
+                                       const uint32_t options);
+
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_DISASSEMBLE_H_
diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp
index 77ee133..d2f4b33 100644
--- a/source/opt/basic_block.cpp
+++ b/source/opt/basic_block.cpp
@@ -15,9 +15,12 @@
 #include "basic_block.h"
 #include "function.h"
 #include "module.h"
+#include "reflect.h"
 
 #include "make_unique.h"
 
+#include <ostream>
+
 namespace spvtools {
 namespace ir {
 
@@ -155,5 +158,15 @@
   return cbid;
 }
 
+std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
+  block.ForEachInst([&str](const ir::Instruction* inst) {
+    str << *inst;
+    if (!IsTerminatorInst(inst->opcode())) {
+      str << std::endl;
+    }
+  });
+  return str;
+}
+
 }  // namespace ir
 }  // namespace spvtools
diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h
index cfff902..2e27a4b 100644
--- a/source/opt/basic_block.h
+++ b/source/opt/basic_block.h
@@ -148,6 +148,7 @@
 
   // Returns the terminator instruction.  Assumes the terminator exists.
   Instruction* terminator() { return &*tail(); }
+  const Instruction* terminator() const { return &*ctail(); }
 
   // Returns true if this basic block exits this function and returns to its
   // caller.
@@ -165,6 +166,9 @@
   InstructionList insts_;
 };
 
+// Pretty-prints |block| to |str|. Returns |str|.
+std::ostream& operator<<(std::ostream& str, const BasicBlock& block);
+
 inline BasicBlock::BasicBlock(std::unique_ptr<Instruction> label)
     : function_(nullptr), label_(std::move(label)) {}
 
diff --git a/source/opt/function.cpp b/source/opt/function.cpp
index 4fc8729..d8e4389 100644
--- a/source/opt/function.cpp
+++ b/source/opt/function.cpp
@@ -16,6 +16,8 @@
 
 #include "make_unique.h"
 
+#include <ostream>
+
 namespace spvtools {
 namespace ir {
 
@@ -75,5 +77,15 @@
         ->ForEachInst(f, run_on_debug_line_insts);
 }
 
+std::ostream& operator<<(std::ostream& str, const Function& func) {
+  func.ForEachInst([&str](const ir::Instruction* inst) {
+    str << *inst;
+    if (inst->opcode() != SpvOpFunctionEnd) {
+      str << std::endl;
+    }
+  });
+  return str;
+}
+
 }  // namespace ir
 }  // namespace spvtools
diff --git a/source/opt/function.h b/source/opt/function.h
index 8caa3e3..0da62a8 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -112,6 +112,9 @@
   std::unique_ptr<Instruction> end_inst_;
 };
 
+// Pretty-prints |func| to |str|. Returns |str|.
+std::ostream& operator<<(std::ostream& str, const Function& func);
+
 inline Function::Function(std::unique_ptr<Instruction> def_inst)
     : module_(nullptr), def_inst_(std::move(def_inst)), end_inst_() {}
 
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp
index 2b83ca1..8321561 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -14,6 +14,7 @@
 
 #include <initializer_list>
 
+#include "disassemble.h"
 #include "fold.h"
 #include "instruction.h"
 #include "ir_context.h"
@@ -476,5 +477,27 @@
   return opt::IsFoldableType(type);
 }
 
+std::string Instruction::PrettyPrint(uint32_t options) const {
+  // Convert the module to binary.
+  std::vector<uint32_t> module_binary;
+  context()->module()->ToBinary(&module_binary, /* skip_nop = */ false);
+
+  // Convert the instruction to binary. This is used to identify the correct
+  // stream of words to output from the module.
+  std::vector<uint32_t> inst_binary;
+  ToBinaryWithoutAttachedDebugInsts(&inst_binary);
+
+  // Do not generate a header.
+  return spvInstructionBinaryToText(
+      context()->grammar().target_env(), inst_binary.data(), inst_binary.size(),
+      module_binary.data(), module_binary.size(),
+      options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+}
+
+std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst) {
+  str << inst.PrettyPrint();
+  return str;
+}
+
 }  // namespace ir
 }  // namespace spvtools
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 61de1f9..30b3d78 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -351,6 +351,15 @@
   // Spec constant.
   inline bool IsConstant() const;
 
+  // Pretty-prints |inst|.
+  //
+  // Provides the disassembly of a specific instruction. Utilizes |inst|'s
+  // context to provide the correct interpretation of types, constants, etc.
+  //
+  // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
+  // is always added to |options|.
+  std::string PrettyPrint(uint32_t options = 0u) const;
+
  private:
   // Returns the total count of result type id and result id.
   uint32_t TypeResultIdCount() const {
@@ -388,6 +397,14 @@
   friend InstructionList;
 };
 
+// Pretty-prints |inst| to |str| and returns |str|.
+//
+// Provides the disassembly of a specific instruction. Utilizes |inst|'s context
+// to provide the correct interpretation of types, constants, etc.
+//
+// Disassembly uses raw ids (not pretty printed names).
+std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst);
+
 inline bool Instruction::operator==(const Instruction& other) const {
   return unique_id() == other.unique_id();
 }
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 98e1433..6602ddf 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -391,6 +391,9 @@
     return feature_mgr_.get();
   }
 
+  // Returns the grammar for this context.
+  const libspirv::AssemblyGrammar& grammar() const { return grammar_; }
+
  private:
   // Builds the def-use manager from scratch, even if it was already valid.
   void BuildDefUseManager() {
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index ba31384..fea49ef 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -16,6 +16,7 @@
 
 #include <algorithm>
 #include <cstring>
+#include <ostream>
 
 #include "operand.h"
 #include "reflect.h"
@@ -158,5 +159,15 @@
   return 0;
 }
 
+std::ostream& operator<<(std::ostream& str, const Module& module) {
+  module.ForEachInst([&str](const ir::Instruction* inst) {
+    str << *inst;
+    if (inst->opcode() != SpvOpFunctionEnd) {
+      str << std::endl;
+    }
+  });
+  return str;
+}
+
 }  // namespace ir
 }  // namespace spvtools
diff --git a/source/opt/module.h b/source/opt/module.h
index 99ed3a2..163c4e3 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -273,6 +273,9 @@
   std::vector<std::unique_ptr<Function>> functions_;
 };
 
+// Pretty-prints |module| to |str|. Returns |str|.
+std::ostream& operator<<(std::ostream& str, const Module& module);
+
 inline void Module::AddCapability(std::unique_ptr<Instruction> c) {
   capabilities_.push_back(std::move(c));
 }
diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp
index 43be818..177b45b 100644
--- a/test/opt/module_test.cpp
+++ b/test/opt/module_test.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <sstream>
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -26,10 +27,10 @@
 
 namespace {
 
+using ::testing::Eq;
 using spvtest::GetIdBound;
 using spvtools::ir::IRContext;
 using spvtools::ir::Module;
-using ::testing::Eq;
 
 TEST(ModuleTest, SetIdBound) {
   Module m;
@@ -46,7 +47,8 @@
 // Returns an IRContext owning the module formed by assembling the given text,
 // then loading the result.
 inline std::unique_ptr<IRContext> BuildModule(std::string text) {
-  return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
+  return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                               SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
 }
 
 TEST(ModuleTest, ComputeIdBound) {
@@ -74,4 +76,68 @@
                 ->ComputeIdBound());
 }
 
+TEST(ModuleTest, OstreamOperator) {
+  const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %7 "restrict"
+OpDecorate %8 Restrict
+%9 = OpTypeVoid
+%10 = OpTypeInt 32 0
+%11 = OpTypeStruct %10 %10
+%12 = OpTypePointer Function %10
+%13 = OpTypePointer Function %11
+%14 = OpConstant %10 0
+%15 = OpConstant %10 1
+%7 = OpTypeFunction %9
+%1 = OpFunction %9 None %7
+%2 = OpLabel
+%8 = OpVariable %13 Function
+%3 = OpAccessChain %12 %8 %14
+%4 = OpLoad %10 %3
+%5 = OpAccessChain %12 %8 %15
+%6 = OpLoad %10 %5
+OpReturn
+OpFunctionEnd)";
+
+  std::string s;
+  std::ostringstream str(s);
+  str << *BuildModule(text)->module();
+  EXPECT_EQ(text, str.str());
+}
+
+TEST(ModuleTest, OstreamOperatorInt64) {
+  const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpCapability Int64
+OpMemoryModel Logical GLSL450
+OpName %7 "restrict"
+OpDecorate %5 Restrict
+%9 = OpTypeVoid
+%10 = OpTypeInt 64 0
+%11 = OpTypeStruct %10 %10
+%12 = OpTypePointer Function %10
+%13 = OpTypePointer Function %11
+%14 = OpConstant %10 0
+%15 = OpConstant %10 1
+%16 = OpConstant %10 4294967297
+%7 = OpTypeFunction %9
+%1 = OpFunction %9 None %7
+%2 = OpLabel
+%5 = OpVariable %12 Function
+%6 = OpLoad %10 %5
+OpSelectionMerge %3 None
+OpSwitch %6 %3 4294967297 %4
+%4 = OpLabel
+OpBranch %3
+%3 = OpLabel
+OpReturn
+OpFunctionEnd)";
+
+  std::string s;
+  std::ostringstream str(s);
+  str << *BuildModule(text)->module();
+  EXPECT_EQ(text, str.str());
+}
+
 }  // anonymous namespace
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index a2ed459..de7bf47 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -31,9 +31,9 @@
            'LunarG Inc.',
            'Google Inc.',
            'Pierre Moreau']
-CURRENT_YEAR='2017'
+CURRENT_YEAR='2018'
 
-YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017)'
+YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2018)'
 COPYRIGHT_RE = re.compile(
         'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))