From 2e28feed94d6fae04c1694b89a81d457004b29c3 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 10 Jul 2025 05:24:59 +0000 Subject: [PATCH] Make `log()` use the `FmtString` infrastructure. Now `log()` supports `std::string`. We have to fix a few places where the format parameter was not a compile time constant. This is mostly trivial. --- kernel/io.h | 1 + kernel/log.cc | 45 ++++++++++++++++++++---------------- kernel/log.h | 35 +++++++++++++++++++++++----- passes/cmds/logcmd.cc | 10 ++++---- passes/cmds/stat.cc | 2 +- passes/equiv/equiv_simple.cc | 2 +- passes/sat/sat.cc | 4 ++-- passes/techmap/dfflibmap.cc | 4 ++-- passes/tests/test_cell.cc | 2 +- 9 files changed, 67 insertions(+), 38 deletions(-) diff --git a/kernel/io.h b/kernel/io.h index ea2499e43..a05ed731e 100644 --- a/kernel/io.h +++ b/kernel/io.h @@ -423,6 +423,7 @@ public: { return format_emit_toplevel(fmt, has_escapes, specs, args...); } + std::string_view format_string() const { return fmt; } private: std::string_view fmt; bool has_escapes = false; diff --git a/kernel/log.cc b/kernel/log.cc index b4c35d25a..131291a3c 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -101,17 +101,16 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) } #endif -static void logv(const char *format, va_list ap) -{ - while (format[0] == '\n' && format[1] != 0) { - log("\n"); - format++; +static void logv_string(std::string_view format, std::string str) { + size_t remove_leading = 0; + while (format.size() > 1 && format[0] == '\n') { + logv_string("\n", "\n"); + format = format.substr(1); + ++remove_leading; + } + if (remove_leading > 0) { + str = str.substr(remove_leading); } - - if (log_make_debug && !ys_debug(1)) - return; - - std::string str = vstringf(format, ap); if (str.empty()) return; @@ -144,13 +143,13 @@ static void logv(const char *format, va_list ap) time_str += stringf("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec)); } - if (format[0] && format[strlen(format)-1] == '\n') + if (!format.empty() && format[format.size() - 1] == '\n') next_print_log = true; // Special case to detect newlines in Python log output, since // the binding always calls `log("%s", payload)` and the newline // is then in the first formatted argument - if (!strcmp(format, "%s") && str.back() == '\n') + if (format == "%s" && str.back() == '\n') next_print_log = true; for (auto f : log_files) @@ -203,6 +202,20 @@ static void logv(const char *format, va_list ap) } } +static void logv(const char *format, va_list ap) +{ + if (log_make_debug && !ys_debug(1)) + return; + logv_string(format, vstringf(format, ap)); +} + +void log_formatted_string(std::string_view format, std::string str) +{ + if (log_make_debug && !ys_debug(1)) + return; + logv_string(format, std::move(str)); +} + static void logv_header(RTLIL::Design *design, const char *format, va_list ap) { bool pop_errfile = false; @@ -401,14 +414,6 @@ void log_file_error(const string &filename, int lineno, logv_file_error(filename, lineno, format, ap); } -void log(const char *format, ...) -{ - va_list ap; - va_start(ap, format); - logv(format, ap); - va_end(ap); -} - void log_header(RTLIL::Design *design, const char *format, ...) { va_list ap; diff --git a/kernel/log.h b/kernel/log.h index b6451bcac..d1a438e77 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -121,7 +121,7 @@ extern int log_debug_suppressed; [[noreturn]] void logv_file_error(const string &filename, int lineno, 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)); void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); @@ -145,6 +145,15 @@ static inline bool ys_debug(int = 0) { return false; } #endif # define log_debug(...) do { if (ys_debug(1)) log(__VA_ARGS__); } while (0) +void log_formatted_string(std::string_view format, std::string str); +template +inline void log(FmtString...> fmt, Args... args) +{ + if (log_make_debug && !ys_debug(1)) + return; + log_formatted_string(fmt.format_string(), fmt.format(args...)); +} + static inline void log_suppressed() { if (log_debug_suppressed && !log_make_debug) { log("\n", log_debug_suppressed); @@ -345,8 +354,22 @@ static inline void log_dump_val_worker(unsigned long int v) { log("%lu", v); } static inline void log_dump_val_worker(long long int v) { log("%lld", v); } static inline void log_dump_val_worker(unsigned long long int v) { log("%lld", v); } #endif -static inline void log_dump_val_worker(char c) { log(c >= 32 && c < 127 ? "'%c'" : "'\\x%02x'", c); } -static inline void log_dump_val_worker(unsigned char c) { log(c >= 32 && c < 127 ? "'%c'" : "'\\x%02x'", c); } +static inline void log_dump_val_worker(char c) +{ + if (c >= 32 && c < 127) { + log("'%c'", c); + } else { + log("'\\x%02x'", c); + } +} +static inline void log_dump_val_worker(unsigned char c) +{ + if (c >= 32 && c < 127) { + log("'%c'", c); + } else { + log("'\\x%02x'", c); + } +} static inline void log_dump_val_worker(bool v) { log("%s", v ? "true" : "false"); } static inline void log_dump_val_worker(double v) { log("%f", v); } static inline void log_dump_val_worker(char *v) { log("%s", v); } @@ -368,7 +391,7 @@ static inline void log_dump_val_worker(dict &v) { log("{"); bool first = true; for (auto &it : v) { - log(first ? " " : ", "); + log("%s ", first ? "" : ","); log_dump_val_worker(it.first); log(": "); log_dump_val_worker(it.second); @@ -382,7 +405,7 @@ static inline void log_dump_val_worker(pool &v) { log("{"); bool first = true; for (auto &it : v) { - log(first ? " " : ", "); + log("%s ", first ? "" : ","); log_dump_val_worker(it); first = false; } @@ -394,7 +417,7 @@ static inline void log_dump_val_worker(std::vector &v) { log("{"); bool first = true; for (auto &it : v) { - log(first ? " " : ", "); + log("%s ", first ? "" : ","); log_dump_val_worker(it); first = false; } diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc index 3b82ac48c..9f57eb455 100644 --- a/passes/cmds/logcmd.cc +++ b/passes/cmds/logcmd.cc @@ -97,13 +97,13 @@ struct LogPass : public Pass { text += args[argidx] + ' '; if (!text.empty()) text.resize(text.size()-1); - const char *fmtline = newline ? "%s\n" : "%s"; + const char *line_end = newline ? "\n" : ""; - if (to_stdout) fprintf(stdout, fmtline, text.c_str()); - if (to_stderr) fprintf(stderr, fmtline, text.c_str()); + if (to_stdout) fprintf(stdout, "%s%s", text.c_str(), line_end); + if (to_stderr) fprintf(stderr, "%s%s", text.c_str(), line_end); if (to_log) { - if (!header) log(fmtline, text.c_str()); - else log_header(design, fmtline, text.c_str()); + if (!header) log("%s%s", text.c_str(), line_end); + else log_header(design, "%s%s", text.c_str(), line_end); } } } LogPass; diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 6b93621f1..bb12b237b 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -474,7 +474,7 @@ struct StatPass : public Pass { if (json_mode) { log("\n"); - log(top_mod == nullptr ? " }\n" : " },\n"); + log("%s", top_mod == nullptr ? " }\n" : " },\n"); } if (top_mod != nullptr) diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 59974a1e6..ff5f04fed 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -202,7 +202,7 @@ struct EquivSimpleWorker log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses()); if (!ez->solve(ez_context)) { - log(verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n"); + log("%s", verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n"); equiv_cell->setPort(ID::B, equiv_cell->getPort(ID::A)); ez->assume(ez->NOT(ez_context)); return true; diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 0c2143fc9..215aac21b 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -639,10 +639,10 @@ struct SatHelper "---------------------------------------------------------------------------------------------------" "---------------------------------------------------------------------------------------------------"; if (last_timestep == -2) { - log(max_timestep > 0 ? " Time " : " "); + log("%s", max_timestep > 0 ? " Time " : " "); log("%-*s %11s %9s %*s\n", maxModelName+5, "Signal Name", "Dec", "Hex", maxModelWidth+3, "Bin"); } - log(max_timestep > 0 ? " ---- " : " "); + log("%s", max_timestep > 0 ? " ---- " : " "); log("%*.*s %11.11s %9.9s %*.*s\n", maxModelName+5, maxModelName+5, hline, hline, hline, maxModelWidth+3, maxModelWidth+3, hline); last_timestep = info.timestep; diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 409ac7865..e68620260 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -553,11 +553,11 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module) new_cell->setPort("\\" + port.first, sig); } - stats[stringf(" mapped %%d %s cells to %s cells.\n", cell_type.c_str(), new_cell->type.c_str())]++; + stats[stringf("%s cells to %s cells", cell_type.c_str(), new_cell->type.c_str())]++; } for (auto &stat: stats) - log(stat.first.c_str(), stat.second); + log(" mapped %d %s.\n", stat.second, stat.first); } struct DfflibmapPass : public Pass { diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index a34eafc2f..ded0398c7 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -522,7 +522,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: for (int i = 0; i < 64; i++) { - log(verbose ? "\n" : "."); + log("%s", verbose ? "\n" : "."); gold_ce.clear(); gate_ce.clear();