Merge pull request #3290 from mpasternacki/bugfix/freebsd-build
[yosys.git] / kernel / log.h
index 603938f4ca6b2e50be5bb52aabeb0a20c9d26422..ea14028dd1fa5d504c115bfd8028cc6dc2103a63 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  yosys -- Yosys Open SYnthesis Suite
  *
- *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com>
  *
  *  Permission to use, copy, modify, and/or distribute this software for any
  *  purpose with or without fee is hereby granted, provided that the above
 #define LOG_H
 
 #include <time.h>
-#include <regex>
 
-#ifndef _WIN32
+// In the libstdc++ headers that are provided by GCC 4.8, std::regex is not
+// working correctly. In order to make features using regular expressions
+// work, a replacement regex library is used. Just checking for GCC version
+// is not enough though, because at least on RHEL7/CentOS7 even when compiling
+// with Clang instead of GCC, the GCC 4.8 headers are still used for std::regex.
+// We have to check the version of the libstdc++ headers specifically, not the
+// compiler version. GCC headers of libstdc++ before version 3.4 define
+// __GLIBCPP__, later versions define __GLIBCXX__. GCC 7 and newer additionaly
+// define _GLIBCXX_RELEASE with a version number.
+// Include limits std C++ header, so we get the version macros defined:
+#if defined(__cplusplus)
+#  include <limits>
+#endif
+// Check if libstdc++ is from GCC
+#if defined(__GLIBCPP__) || defined(__GLIBCXX__)
+// Check if version could be 4.8 or lower (this also matches for some 4.9 and
+// 5.0 releases). See:
+// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning
+#  if !defined(_GLIBCXX_RELEASE) && (defined(__GLIBCPP__) || __GLIBCXX__ <= 20150623)
+#    define YS_HAS_BAD_STD_REGEX
+#  endif
+#endif
+#if defined(YS_HAS_BAD_STD_REGEX)
+       #include <boost/xpressive/xpressive.hpp>
+       #define YS_REGEX_TYPE boost::xpressive::sregex
+       #define YS_REGEX_MATCH_TYPE boost::xpressive::smatch
+       #define YS_REGEX_NS boost::xpressive
+       #define YS_REGEX_COMPILE(param) boost::xpressive::sregex::compile(param, \
+                                       boost::xpressive::regex_constants::nosubs | \
+                                       boost::xpressive::regex_constants::optimize)
+       #define YS_REGEX_COMPILE_WITH_SUBS(param) boost::xpressive::sregex::compile(param, \
+                                       boost::xpressive::regex_constants::optimize)
+# else
+       #include <regex>
+       #define YS_REGEX_TYPE std::regex
+       #define YS_REGEX_MATCH_TYPE std::smatch
+       #define YS_REGEX_NS std
+       #define YS_REGEX_COMPILE(param) std::regex(param, \
+                                       std::regex_constants::nosubs | \
+                                       std::regex_constants::optimize | \
+                                       std::regex_constants::egrep)
+       #define YS_REGEX_COMPILE_WITH_SUBS(param) std::regex(param, \
+                                       std::regex_constants::optimize | \
+                                       std::regex_constants::egrep)
+#endif
+
+#if defined(_WIN32)
+#  include <intrin.h>
+#else
 #  include <sys/time.h>
 #  include <sys/resource.h>
+#  if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#    include <signal.h>
+#  endif
 #endif
 
 #if defined(_MSC_VER)
@@ -44,12 +94,47 @@ YOSYS_NAMESPACE_BEGIN
 #define S__LINE__sub1(x) S__LINE__sub2(x)
 #define S__LINE__ S__LINE__sub1(__LINE__)
 
+// YS_DEBUGTRAP is a macro that is functionally equivalent to a breakpoint
+// if the platform provides such functionality, and does nothing otherwise.
+// If no debugger is attached, it starts a just-in-time debugger if available,
+// and crashes the process otherwise.
+#if defined(_WIN32)
+# define YS_DEBUGTRAP __debugbreak()
+#else
+# ifndef __has_builtin
+// __has_builtin is a GCC/Clang extension; on a different compiler (or old enough GCC/Clang)
+// that does not have it, using __has_builtin(...) is a syntax error.
+#  define __has_builtin(x) 0
+# endif
+# if __has_builtin(__builtin_debugtrap)
+#  define YS_DEBUGTRAP __builtin_debugtrap()
+# elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#  define YS_DEBUGTRAP raise(SIGTRAP)
+# else
+#  define YS_DEBUGTRAP do {} while(0)
+# endif
+#endif
+
+// YS_DEBUGTRAP_IF_DEBUGGING is a macro that is functionally equivalent to a breakpoint
+// if a debugger is attached, and does nothing otherwise.
+#if defined(_WIN32)
+# define YS_DEBUGTRAP_IF_DEBUGGING do { if (IsDebuggerPresent()) DebugBreak(); } while(0)
+# elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+// There is no reliable (or portable) *nix equivalent of IsDebuggerPresent(). However,
+// debuggers will stop when SIGTRAP is raised, even if the action is set to ignore.
+# define YS_DEBUGTRAP_IF_DEBUGGING do { \
+               auto old = signal(SIGTRAP, SIG_IGN); raise(SIGTRAP); signal(SIGTRAP, old); \
+       } while(0)
+#else
+# define YS_DEBUGTRAP_IF_DEBUGGING do {} while(0)
+#endif
+
 struct log_cmd_error_exception { };
 
 extern std::vector<FILE*> log_files;
 extern std::vector<std::ostream*> log_streams;
 extern std::map<std::string, std::set<std::string>> log_hdump;
-extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
+extern std::vector<YS_REGEX_TYPE> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
 extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
 extern int log_warnings_count;
 extern int log_warnings_count_noexpect;
@@ -74,7 +159,7 @@ void logv(const char *format, va_list ap);
 void logv_header(RTLIL::Design *design, const char *format, va_list ap);
 void logv_warning(const char *format, va_list ap);
 void logv_warning_noprefix(const char *format, va_list ap);
-YS_NORETURN void logv_error(const char *format, va_list ap) YS_ATTRIBUTE(noreturn);
+[[noreturn]] void logv_error(const char *format, va_list ap);
 
 void log(const char *format, ...)  YS_ATTRIBUTE(format(printf, 1, 2));
 void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3));
@@ -86,17 +171,16 @@ void log_file_warning(const std::string &filename, int lineno, const char *forma
 void log_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
 
 void log_warning_noprefix(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
-YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
-void log_file_error(const string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4), noreturn);
-YS_NORETURN void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
+[[noreturn]] void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
+[[noreturn]] void log_file_error(const string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
+[[noreturn]] void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
 
 #ifndef NDEBUG
 static inline bool ys_debug(int n = 0) { if (log_force_debug) return true; log_debug_suppressed += n; return false; }
-#  define log_debug(...) do { if (ys_debug(1)) log(__VA_ARGS__); } while (0)
 #else
 static inline bool ys_debug(int = 0) { return false; }
-#  define log_debug(_fmt, ...) do { } while (0)
 #endif
+#  define log_debug(...) do { if (ys_debug(1)) log(__VA_ARGS__); } while (0)
 
 static inline void log_suppressed() {
        if (log_debug_suppressed && !log_make_debug) {
@@ -139,19 +223,16 @@ void log_flush();
 
 struct LogExpectedItem
 {
-       LogExpectedItem(std::string pattern, int expected) :
-               expected_count(expected),
-               current_count(0),
-               pattern(pattern)
-       {
-       }
+       LogExpectedItem(const YS_REGEX_TYPE &pat, int expected) :
+                       pattern(pat), expected_count(expected), current_count(0) {}
+       LogExpectedItem() : expected_count(0), current_count(0) {}
 
+       YS_REGEX_TYPE pattern;
        int expected_count;
        int current_count;
-       std::string pattern;
 };
 
-extern std::vector<std::pair<std::regex,LogExpectedItem>> log_expect_log, log_expect_warning, log_expect_error;
+extern dict<std::string, LogExpectedItem> log_expect_log, log_expect_warning, log_expect_error;
 void log_check_expected();
 
 const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
@@ -174,7 +255,7 @@ static inline void log_assert_worker(bool cond, const char *expr, const char *fi
 }
 #  define log_assert(_assert_expr_) YOSYS_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)
 #else
-#  define log_assert(_assert_expr_)
+#  define log_assert(_assert_expr_) do { if (0) { (void)(_assert_expr_); } } while(0)
 #endif
 
 #define log_abort() YOSYS_NAMESPACE_PREFIX log_error("Abort in %s:%d.\n", __FILE__, __LINE__)
@@ -246,19 +327,17 @@ struct PerformanceTimer
        static int64_t query() {
 #  ifdef _WIN32
                return 0;
-#  elif defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
-               struct timespec ts;
-               clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
-               return int64_t(ts.tv_sec)*1000000000 + ts.tv_nsec;
 #  elif defined(RUSAGE_SELF)
                struct rusage rusage;
-               int64_t t;
-               if (getrusage(RUSAGE_SELF, &rusage) == -1) {
-                       log_cmd_error("getrusage failed!\n");
-                       log_abort();
+               int64_t t = 0;
+               for (int who : {RUSAGE_SELF, RUSAGE_CHILDREN}) {
+                       if (getrusage(who, &rusage) == -1) {
+                               log_cmd_error("getrusage failed!\n");
+                               log_abort();
+                       }
+                       t += 1000000000ULL * (int64_t) rusage.ru_utime.tv_sec + (int64_t) rusage.ru_utime.tv_usec * 1000ULL;
+                       t += 1000000000ULL * (int64_t) rusage.ru_stime.tv_sec + (int64_t) rusage.ru_stime.tv_usec * 1000ULL;
                }
-               t = 1000000000ULL * (int64_t) rusage.ru_utime.tv_sec + (int64_t) rusage.ru_utime.tv_usec * 1000ULL;
-               t += 1000000000ULL * (int64_t) rusage.ru_stime.tv_sec + (int64_t) rusage.ru_stime.tv_usec * 1000ULL;
                return t;
 #  else
 #    error "Don't know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?)."
@@ -309,7 +388,7 @@ static inline void log_dump_val_worker(char *v) { log("%s", v); }
 static inline void log_dump_val_worker(const char *v) { log("%s", v); }
 static inline void log_dump_val_worker(std::string v) { log("%s", v.c_str()); }
 static inline void log_dump_val_worker(PerformanceTimer p) { log("%f seconds", p.sec()); }
-static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); }
+static inline void log_dump_args_worker(const char *p) { log_assert(*p == 0); }
 void log_dump_val_worker(RTLIL::IdString v);
 void log_dump_val_worker(RTLIL::SigSpec v);
 void log_dump_val_worker(RTLIL::State v);