diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 620b530b0..c9c644a52 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1072,21 +1072,43 @@ struct CxxrtlWorker { dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1>{1u}) {\n"; inc_indent(); - f << indent << "auto formatter = [&](struct performer *performer) {\n"; + dict fmt_args; + f << indent << "struct : public lazy_fmt {\n"; inc_indent(); - fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }, "performer"); + f << indent << "std::string operator() () const override {\n"; + inc_indent(); + fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) { + if (sig.size() == 0) + f << "value<0>()"; + else { + std::string arg_name = "arg" + std::to_string(fmt_args.size()); + fmt_args[arg_name] = sig; + f << arg_name; + } + }, "performer"); + dec_indent(); + f << indent << "}\n"; + f << indent << "struct performer *performer;\n"; + for (auto arg : fmt_args) + f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n"; dec_indent(); - f << indent << "};\n"; + f << indent << "} formatter;\n"; + f << indent << "formatter.performer = performer;\n"; + for (auto arg : fmt_args) { + f << indent << "formatter." << arg.first << " = "; + dump_sigspec_rhs(arg.second); + f << ";\n"; + } f << indent << "if (performer) {\n"; inc_indent(); f << indent << "static const metadata_map attributes = "; dump_metadata_map(cell->attributes); f << ";\n"; - f << indent << "performer->on_print(formatter(performer), attributes);\n"; + f << indent << "performer->on_print(formatter, attributes);\n"; dec_indent(); f << indent << "} else {\n"; inc_indent(); - f << indent << print_output << " << formatter(performer);\n"; + f << indent << print_output << " << formatter();\n"; dec_indent(); f << indent << "}\n"; dec_indent(); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index a8a011d15..550571497 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -945,16 +945,25 @@ struct metadata { typedef std::map metadata_map; +struct performer; + +// An object that allows formatting a string lazily. +struct lazy_fmt { + virtual std::string operator() () const = 0; +}; + // An object that can be passed to a `eval()` method in order to act on side effects. struct performer { - // Called to evaluate a Verilog `$time` expression. + // Called by generated formatting code to evaluate a Verilog `$time` expression. virtual int64_t vlog_time() const { return 0; } - // Called to evaluate a Verilog `$realtime` expression. + // Called by generated formatting code to evaluate a Verilog `$realtime` expression. virtual double vlog_realtime() const { return vlog_time(); } // Called when a `$print` cell is triggered. - virtual void on_print(const std::string &output, const metadata_map &attributes) { std::cout << output; } + virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) { + std::cout << formatter(); + } }; // An object that can be passed to a `commit()` method in order to produce a replay log of every state change in diff --git a/tests/fmt/always_full_tb.cc b/tests/fmt/always_full_tb.cc index 1dd80d0a2..94991ca25 100644 --- a/tests/fmt/always_full_tb.cc +++ b/tests/fmt/always_full_tb.cc @@ -4,7 +4,7 @@ int main() { struct : public performer { int64_t vlog_time() const override { return 1; } - void on_print(const std::string &output, const cxxrtl::metadata_map &) override { std::cerr << output; } + void on_print(const lazy_fmt &output, const cxxrtl::metadata_map &) override { std::cerr << output(); } } performer; cxxrtl_design::p_always__full uut;