Home | History | Annotate | Download | only in opt
      1 // Copyright (c) 2016 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #ifndef SPIRV_TOOLS_LOG_H_
     16 #define SPIRV_TOOLS_LOG_H_
     17 
     18 #include <cstdio>
     19 #include <cstdlib>
     20 #include <utility>
     21 #include <vector>
     22 
     23 #include "spirv-tools/libspirv.hpp"
     24 
     25 // Asserts the given condition is true. Otherwise, sends a message to the
     26 // consumer and exits the problem with failure code. Accepts the following
     27 // formats:
     28 //
     29 // SPIRV_ASSERT(<message-consumer>, <condition-expression>);
     30 // SPIRV_ASSERT(<message-consumer>, <condition-expression>, <message>);
     31 // SPIRV_ASSERT(<message-consumer>, <condition-expression>,
     32 //              <message-format>,   <variable-arguments>);
     33 //
     34 // In the third format, the number of <variable-arguments> cannot exceed (5 -
     35 // 2). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below.
     36 #if !defined(NDEBUG)
     37 #define SPIRV_ASSERT(consumer, ...) SPIRV_ASSERT_IMPL(consumer, __VA_ARGS__)
     38 #else
     39 #define SPIRV_ASSERT(consumer, ...)
     40 #endif
     41 
     42 // Logs a debug message to the consumer. Accepts the following formats:
     43 //
     44 // SPIRV_DEBUG(<message-consumer>, <message>);
     45 // SPIRV_DEBUG(<message-consumer>, <message-format>, <variable-arguments>);
     46 //
     47 // In the second format, the number of <variable-arguments> cannot exceed (5 -
     48 // 1). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below.
     49 #if !defined(NDEBUG) && defined(SPIRV_LOG_DEBUG)
     50 #define SPIRV_DEBUG(consumer, ...) SPIRV_DEBUG_IMPL(consumer, __VA_ARGS__)
     51 #else
     52 #define SPIRV_DEBUG(consumer, ...)
     53 #endif
     54 
     55 // Logs an error message to the consumer saying the given feature is
     56 // unimplemented.
     57 #define SPIRV_UNIMPLEMENTED(consumer, feature)                  \
     58   do {                                                          \
     59     spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__,   \
     60                   {__LINE__, 0, 0}, "unimplemented: " feature); \
     61   } while (0)
     62 
     63 // Logs an error message to the consumer saying the code location
     64 // should be unreachable.
     65 #define SPIRV_UNREACHABLE(consumer)                           \
     66   do {                                                        \
     67     spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \
     68                   {__LINE__, 0, 0}, "unreachable");           \
     69   } while (0)
     70 
     71 // Helper macros for concatenating arguments.
     72 #define SPIRV_CONCATENATE(a, b) SPIRV_CONCATENATE_(a, b)
     73 #define SPIRV_CONCATENATE_(a, b) a##b
     74 
     75 // Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler.
     76 #define PP_EXPAND(x) x
     77 
     78 namespace spvtools {
     79 
     80 // Calls the given |consumer| by supplying  the |message|. The |message| is from
     81 // the given |source| and |location| and of the given severity |level|.
     82 inline void Log(const MessageConsumer& consumer, spv_message_level_t level,
     83                 const char* source, const spv_position_t& position,
     84                 const char* message) {
     85   if (consumer != nullptr) consumer(level, source, position, message);
     86 }
     87 
     88 // Calls the given |consumer| by supplying the message composed according to the
     89 // given |format|. The |message| is from the given |source| and |location| and
     90 // of the given severity |level|.
     91 template <typename... Args>
     92 void Logf(const MessageConsumer& consumer, spv_message_level_t level,
     93           const char* source, const spv_position_t& position,
     94           const char* format, Args&&... args) {
     95 #if defined(_MSC_VER) && _MSC_VER < 1900
     96 // Sadly, snprintf() is not supported until Visual Studio 2015!
     97 #define snprintf _snprintf
     98 #endif
     99 
    100   enum { kInitBufferSize = 256 };
    101 
    102   char message[kInitBufferSize];
    103   const int size =
    104       snprintf(message, kInitBufferSize, format, std::forward<Args>(args)...);
    105 
    106   if (size >= 0 && size < kInitBufferSize) {
    107     Log(consumer, level, source, position, message);
    108     return;
    109   }
    110 
    111   if (size >= 0) {
    112     // The initial buffer is insufficient.  Allocate a buffer of a larger size,
    113     // and write to it instead.  Force the size to be unsigned to avoid a
    114     // warning in GCC 7.1.
    115     std::vector<char> longer_message(size + 1u);
    116     snprintf(longer_message.data(), longer_message.size(), format,
    117              std::forward<Args>(args)...);
    118     Log(consumer, level, source, position, longer_message.data());
    119     return;
    120   }
    121 
    122   Log(consumer, level, source, position, "cannot compose log message");
    123 
    124 #if defined(_MSC_VER) && _MSC_VER < 1900
    125 #undef snprintf
    126 #endif
    127 }
    128 
    129 // Calls the given |consumer| by supplying  the given error |message|. The
    130 // |message| is from the given |source| and |location|.
    131 inline void Error(const MessageConsumer& consumer, const char* source,
    132                   const spv_position_t& position, const char* message) {
    133   Log(consumer, SPV_MSG_ERROR, source, position, message);
    134 }
    135 
    136 // Calls the given |consumer| by supplying the error message composed according
    137 // to the given |format|. The |message| is from the given |source| and
    138 // |location|.
    139 template <typename... Args>
    140 inline void Errorf(const MessageConsumer& consumer, const char* source,
    141                    const spv_position_t& position, const char* format,
    142                    Args&&... args) {
    143   Logf(consumer, SPV_MSG_ERROR, source, position, format,
    144        std::forward<Args>(args)...);
    145 }
    146 
    147 }  // namespace spvtools
    148 
    149 #define SPIRV_ASSERT_IMPL(consumer, ...)                             \
    150   PP_EXPAND(SPIRV_CONCATENATE(SPIRV_ASSERT_, PP_NARGS(__VA_ARGS__))( \
    151       consumer, __VA_ARGS__))
    152 
    153 #define SPIRV_DEBUG_IMPL(consumer, ...)                             \
    154   PP_EXPAND(SPIRV_CONCATENATE(SPIRV_DEBUG_, PP_NARGS(__VA_ARGS__))( \
    155       consumer, __VA_ARGS__))
    156 
    157 #define SPIRV_ASSERT_1(consumer, condition)                             \
    158   do {                                                                  \
    159     if (!(condition)) {                                                 \
    160       spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__,         \
    161                     {__LINE__, 0, 0}, "assertion failed: " #condition); \
    162       std::exit(EXIT_FAILURE);                                          \
    163     }                                                                   \
    164   } while (0)
    165 
    166 #define SPIRV_ASSERT_2(consumer, condition, message)                 \
    167   do {                                                               \
    168     if (!(condition)) {                                              \
    169       spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__,      \
    170                     {__LINE__, 0, 0}, "assertion failed: " message); \
    171       std::exit(EXIT_FAILURE);                                       \
    172     }                                                                \
    173   } while (0)
    174 
    175 #define SPIRV_ASSERT_more(consumer, condition, format, ...)         \
    176   do {                                                              \
    177     if (!(condition)) {                                             \
    178       spvtools::Logf(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__,    \
    179                      {__LINE__, 0, 0}, "assertion failed: " format, \
    180                      __VA_ARGS__);                                  \
    181       std::exit(EXIT_FAILURE);                                      \
    182     }                                                               \
    183   } while (0)
    184 
    185 #define SPIRV_ASSERT_3(consumer, condition, format, ...) \
    186   SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__)
    187 
    188 #define SPIRV_ASSERT_4(consumer, condition, format, ...) \
    189   SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__)
    190 
    191 #define SPIRV_ASSERT_5(consumer, condition, format, ...) \
    192   SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__)
    193 
    194 #define SPIRV_DEBUG_1(consumer, message)                               \
    195   do {                                                                 \
    196     spvtools::Log(consumer, SPV_MSG_DEBUG, __FILE__, {__LINE__, 0, 0}, \
    197                   message);                                            \
    198   } while (0)
    199 
    200 #define SPIRV_DEBUG_more(consumer, format, ...)                         \
    201   do {                                                                  \
    202     spvtools::Logf(consumer, SPV_MSG_DEBUG, __FILE__, {__LINE__, 0, 0}, \
    203                    format, __VA_ARGS__);                                \
    204   } while (0)
    205 
    206 #define SPIRV_DEBUG_2(consumer, format, ...) \
    207   SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
    208 
    209 #define SPIRV_DEBUG_3(consumer, format, ...) \
    210   SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
    211 
    212 #define SPIRV_DEBUG_4(consumer, format, ...) \
    213   SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
    214 
    215 #define SPIRV_DEBUG_5(consumer, format, ...) \
    216   SPIRV_DEBUG_more(consumer, format, __VA_ARGS__)
    217 
    218 // Macros for counting the number of arguments passed in.
    219 #define PP_NARGS(...) PP_EXPAND(PP_ARG_N(__VA_ARGS__, 5, 4, 3, 2, 1, 0))
    220 #define PP_ARG_N(_1, _2, _3, _4, _5, N, ...) N
    221 
    222 // Tests for making sure that PP_NARGS() behaves as expected.
    223 static_assert(PP_NARGS(0) == 1, "PP_NARGS macro error");
    224 static_assert(PP_NARGS(0, 0) == 2, "PP_NARGS macro error");
    225 static_assert(PP_NARGS(0, 0, 0) == 3, "PP_NARGS macro error");
    226 static_assert(PP_NARGS(0, 0, 0, 0) == 4, "PP_NARGS macro error");
    227 static_assert(PP_NARGS(0, 0, 0, 0, 0) == 5, "PP_NARGS macro error");
    228 static_assert(PP_NARGS(1 + 1, 2, 3 / 3) == 3, "PP_NARGS macro error");
    229 static_assert(PP_NARGS((1, 1), 2, (3, 3)) == 3, "PP_NARGS macro error");
    230 
    231 #endif  // SPIRV_TOOLS_LOG_H_
    232