Add utility macros for logging messages.

Add the following macros for logging purpose:
* SPIRV_ASSERT
* SPIRV_DEBUG
* SPIRV_UNIMPLEMENTED
* SPIRV_UNREACHABLE

The last two is always turned on, while the first two can only
be turned on in debug build.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 171fd29..e881ca5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -81,6 +81,11 @@
   add_definitions(-DSPIRV_COLOR_TERMINAL)
 endif()
 
+option(SPIRV_LOG_DEBUG "Enable excessive debug output" OFF)
+if(${SPIRV_LOG_DEBUG})
+  add_definitions(-DSPIRV_LOG_DEBUG)
+endif()
+
 function(spvtools_default_compile_options TARGET)
   target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS})
 
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 74fee56..431f2d1 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -22,6 +22,7 @@
   instruction.h
   ir_loader.h
   libspirv.hpp
+  log.h
   module.h
   null_pass.h
   reflect.h
diff --git a/source/opt/log.h b/source/opt/log.h
new file mode 100644
index 0000000..2b033fe
--- /dev/null
+++ b/source/opt/log.h
@@ -0,0 +1,202 @@
+// Copyright (c) 2016 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_LOG_H_
+#define SPIRV_TOOLS_LOG_H_
+
+#include <cstdio>
+#include <cstdlib>
+#include <utility>
+#include <vector>
+
+#include "message.h"
+
+// Asserts the given condition is true. Otherwise, send a message to the
+// consumer. Accepts the following formats:
+//
+// SPIRV_ASSERT(<message-consumer>, <condition-expression>);
+// SPIRV_ASSERT(<message-consumer>, <condition-expression>, <message>);
+// SPIRV_ASSERT(<message-consumer>, <condition-expression>,
+//              <message-format>,   <variable-arguments>);
+//
+// In the third format, the number of <variable-arguments> cannot exceed (5 -
+// 2). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below.
+#if !defined(NDEBUG)
+#define SPIRV_ASSERT(consumer, ...) SPIRV_ASSERT_IMPL(consumer, __VA_ARGS__)
+#else
+#define SPIRV_ASSERT(consumer, ...)
+#endif
+
+// Logs a debug message to the consumer. Accepts the following formats:
+//
+// SPIRV_DEBUG(<message-consumer>, <message>);
+// SPIRV_DEBUG(<message-consumer>, <message-format>, <variable-arguments>);
+//
+// In the second format, the number of <variable-arguments> cannot exceed (5 -
+// 1). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below.
+#if !defined(NDEBUG) && defined(SPIRV_LOG_DEBUG)
+#define SPIRV_DEBUG(consumer, ...) SPIRV_DEBUG_IMPL(consumer, __VA_ARGS__)
+#else
+#define SPIRV_DEBUG(consumer, ...)
+#endif
+
+// Logs an error message to the consumer saying the given feature is
+// unimplemented.
+#define SPIRV_UNIMPLEMENTED(consumer, feature)                     \
+  do {                                                             \
+    spvtools::Log(consumer, MessageLevel::InternalError, __FILE__, \
+                  {__LINE__, 0, 0}, "unimplemented: " feature);    \
+  } while (0)
+
+// Logs an error message to the consumer saying the code location
+// should be unreachable.
+#define SPIRV_UNREACHABLE(consumer)                                \
+  do {                                                             \
+    spvtools::Log(consumer, MessageLevel::InternalError, __FILE__, \
+                  {__LINE__, 0, 0}, "unreachable");                \
+  } while (0)
+
+// Helper macros for concatenating arguments.
+#define SPIRV_CONCATENATE(a, b) SPIRV_CONCATENATE_(a, b)
+#define SPIRV_CONCATENATE_(a, b) a##b
+
+// Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler.
+#define PP_EXPAND(x) x
+
+namespace spvtools {
+
+// Calls the given |consumer| by supplying  the |message|. The |message| is from
+// the given |file| and |location| and of the given severity |level|.
+inline void Log(const MessageConsumer& consumer, MessageLevel level,
+                const char* file, const spv_position_t& position,
+                const char* message) {
+  if (consumer != nullptr) consumer(level, file, position, message);
+}
+
+// Calls the given |consumer| by supplying the message composed according to the
+// given |format|. The |message| is from the given |file| and |location| and of
+// the given severity |level|.
+template <typename... Args>
+void Logf(const MessageConsumer& consumer, MessageLevel level, const char* file,
+          const spv_position_t& position, const char* format, Args&&... args) {
+#if defined(_MSC_VER) && _MSC_VER < 1900
+// Sadly, snprintf() is not supported until Visual Studio 2015!
+#define snprintf _snprintf
+#endif
+
+  enum { kInitBufferSize = 256 };
+
+  char message[kInitBufferSize];
+  const int size =
+      snprintf(message, kInitBufferSize, format, std::forward<Args>(args)...);
+
+  if (size >= 0 && size < kInitBufferSize) {
+    Log(consumer, level, file, position, message);
+    return;
+  }
+
+  if (size >= 0) {  // The initial buffer is insufficient.
+    std::vector<char> longer_message(size + 1);
+    snprintf(longer_message.data(), longer_message.size(), format,
+             std::forward<Args>(args)...);
+    Log(consumer, level, file, position, longer_message.data());
+    return;
+  }
+
+  Log(consumer, level, file, position, "cannot compose log message");
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#undef snprintf
+#endif
+}
+
+}  // namespace spvtools
+
+#define SPIRV_ASSERT_IMPL(consumer, ...)                             \
+  PP_EXPAND(SPIRV_CONCATENATE(SPIRV_ASSERT_, PP_NARGS(__VA_ARGS__))( \
+      consumer, __VA_ARGS__))
+
+#define SPIRV_DEBUG_IMPL(consumer, ...)                             \
+  PP_EXPAND(SPIRV_CONCATENATE(SPIRV_DEBUG_, PP_NARGS(__VA_ARGS__))( \
+      consumer, __VA_ARGS__))
+
+#define SPIRV_ASSERT_1(consumer, condition)                             \
+  do {                                                                  \
+    if (!(condition))                                                   \
+      spvtools::Log(consumer, MessageLevel::InternalError, __FILE__,    \
+                    {__LINE__, 0, 0}, "assertion failed: " #condition); \
+  } while (0)
+
+#define SPIRV_ASSERT_2(consumer, condition, message)                 \
+  do {                                                               \
+    if (!(condition))                                                \
+      spvtools::Log(consumer, MessageLevel::InternalError, __FILE__, \
+                    {__LINE__, 0, 0}, "assertion failed: " message); \
+  } while (0)
+
+#define SPIRV_ASSERT_more(consumer, condition, format, ...)           \
+  do {                                                                \
+    if (!(condition))                                                 \
+      spvtools::Logf(consumer, MessageLevel::InternalError, __FILE__, \
+                     {__LINE__, 0, 0}, "assertion failed: " format,   \
+                     __VA_ARGS__);                                    \
+  } while (0)
+
+#define SPIRV_ASSERT_3(consumer, condition, format, ...) \
+  SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__)
+
+#define SPIRV_ASSERT_4(consumer, condition, format, ...) \
+  SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__)
+
+#define SPIRV_ASSERT_5(consumer, condition, format, ...) \
+  SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__)
+
+#define SPIRV_DEBUG_1(consumer, message)                                     \
+  do {                                                                       \
+    spvtools::Log(consumer, MessageLevel::Debug, __FILE__, {__LINE__, 0, 0}, \
+                  message);                                                  \
+  } while (0)
+
+#define SPIRV_DEBUG_more(consumer, format, ...)                               \
+  do {                                                                        \
+    spvtools::Logf(consumer, MessageLevel::Debug, __FILE__, {__LINE__, 0, 0}, \
+                   format, __VA_ARGS__);                                      \
+  } while (0)
+
+#define SPIRV_DEBUG_2(consumer, format, ...) \
+  SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
+
+#define SPIRV_DEBUG_3(consumer, format, ...) \
+  SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
+
+#define SPIRV_DEBUG_4(consumer, format, ...) \
+  SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
+
+#define SPIRV_DEBUG_5(consumer, format, ...) \
+  SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
+
+// Macros for counting the number of arguments passed in.
+#define PP_NARGS(...) PP_EXPAND(PP_ARG_N(__VA_ARGS__, 5, 4, 3, 2, 1, 0))
+#define PP_ARG_N(_1, _2, _3, _4, _5, N, ...) N
+
+// Tests for making sure that PP_NARGS() behaves as expected.
+static_assert(PP_NARGS(0) == 1, "PP_NARGS macro error");
+static_assert(PP_NARGS(0, 0) == 2, "PP_NARGS macro error");
+static_assert(PP_NARGS(0, 0, 0) == 3, "PP_NARGS macro error");
+static_assert(PP_NARGS(0, 0, 0, 0) == 4, "PP_NARGS macro error");
+static_assert(PP_NARGS(0, 0, 0, 0, 0) == 5, "PP_NARGS macro error");
+static_assert(PP_NARGS(1 + 1, 2, 3 / 3) == 3, "PP_NARGS macro error");
+static_assert(PP_NARGS((1, 1), 2, (3, 3)) == 3, "PP_NARGS macro error");
+
+#endif  // SPIRV_TOOLS_LOG_H_
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 65fcfd3..5ce9d9f 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -144,5 +144,10 @@
   SRCS ${CMAKE_CURRENT_SOURCE_DIR}/ParseNumber.cpp
   LIBS ${SPIRV_TOOLS})
 
+add_spvtools_unittest(
+  TARGET log
+  SRCS test_log.cpp
+  LIBS ${SPIRV_TOOLS})
+
 add_subdirectory(opt)
 add_subdirectory(val)
diff --git a/test/test_log.cpp b/test/test_log.cpp
new file mode 100644
index 0000000..ac19549
--- /dev/null
+++ b/test/test_log.cpp
@@ -0,0 +1,111 @@
+// Copyright (c) 2016 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.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "message.h"
+#include "opt/log.h"
+
+namespace {
+
+using namespace spvtools;
+using ::testing::MatchesRegex;
+
+TEST(Log, AssertStatement) {
+  int invocation = 0;
+  auto consumer = [&invocation](MessageLevel level, const char* source,
+                                const spv_position_t&, const char* message) {
+    ++invocation;
+    EXPECT_EQ(MessageLevel::InternalError, level);
+    EXPECT_THAT(source, MatchesRegex(".*test_log.cpp$"));
+    EXPECT_STREQ("assertion failed: 1 + 2 == 5", message);
+  };
+
+  SPIRV_ASSERT(consumer, 1 + 2 == 5);
+#if defined(NDEBUG)
+  (void)consumer;
+  EXPECT_EQ(0, invocation);
+#else
+  EXPECT_EQ(1, invocation);
+#endif
+}
+
+TEST(Log, AssertMessage) {
+  int invocation = 0;
+  auto consumer = [&invocation](MessageLevel level, const char* source,
+                                const spv_position_t&, const char* message) {
+    ++invocation;
+    EXPECT_EQ(MessageLevel::InternalError, level);
+    EXPECT_THAT(source, MatchesRegex(".*test_log.cpp$"));
+    EXPECT_STREQ("assertion failed: happy asserting!", message);
+  };
+
+  SPIRV_ASSERT(consumer, 1 + 2 == 5, "happy asserting!");
+#if defined(NDEBUG)
+  (void)consumer;
+  EXPECT_EQ(0, invocation);
+#else
+  EXPECT_EQ(1, invocation);
+#endif
+}
+
+TEST(Log, AssertFormattedMessage) {
+  int invocation = 0;
+  auto consumer = [&invocation](MessageLevel level, const char* source,
+                                const spv_position_t&, const char* message) {
+    ++invocation;
+    EXPECT_EQ(MessageLevel::InternalError, level);
+    EXPECT_THAT(source, MatchesRegex(".*test_log.cpp$"));
+    EXPECT_STREQ("assertion failed: 1 + 2 actually is 3", message);
+  };
+
+  SPIRV_ASSERT(consumer, 1 + 2 == 5, "1 + 2 actually is %d", 1 + 2);
+#if defined(NDEBUG)
+  (void)consumer;
+  EXPECT_EQ(0, invocation);
+#else
+  EXPECT_EQ(1, invocation);
+#endif
+}
+
+TEST(Log, Unimplemented) {
+  int invocation = 0;
+  auto consumer = [&invocation](MessageLevel level, const char* source,
+                                const spv_position_t&, const char* message) {
+    ++invocation;
+    EXPECT_EQ(MessageLevel::InternalError, level);
+    EXPECT_THAT(source, MatchesRegex(".*test_log.cpp$"));
+    EXPECT_STREQ("unimplemented: the-ultimite-feature", message);
+  };
+
+  SPIRV_UNIMPLEMENTED(consumer, "the-ultimite-feature");
+  EXPECT_EQ(1, invocation);
+}
+
+TEST(Log, Unreachable) {
+  int invocation = 0;
+  auto consumer = [&invocation](MessageLevel level, const char* source,
+                                const spv_position_t&, const char* message) {
+    ++invocation;
+    EXPECT_EQ(MessageLevel::InternalError, level);
+    EXPECT_THAT(source, MatchesRegex(".*test_log.cpp$"));
+    EXPECT_STREQ("unreachable", message);
+  };
+
+  SPIRV_UNREACHABLE(consumer);
+  EXPECT_EQ(1, invocation);
+}
+
+}  // anonymous namespace