From 7b45b3ee8abe1f02574e6bc7dd70ff6f9d12818b Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 3 Jun 2026 09:54:02 +0100 Subject: [PATCH] Optimize string formatting runtime functions Add an overload that takes `const char*` format string. This avoids having to construct/destruct a temporary `std::string` at the call site when calling these functions with a string literal, which is fairly common. This is worth 25% code size on some assertion heavy design. Also use `std::string::clear()` instead of assigning an empty string which is more efficient. --- include/verilated.cpp | 113 +++++++++++++++++++++++++++++++++----- include/verilated_funcs.h | 23 ++++++-- 2 files changed, 117 insertions(+), 19 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index 31cd07ba6..ed2f44b7f 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -998,7 +998,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, bool widthSet = false; bool left = false; size_t width = 0; - output = ""; + output.clear(); output.reserve(format.length()); for (std::string::const_iterator pos = format.cbegin(); pos != format.cend(); ++pos) { if (!inPct && pos[0] == '%') { @@ -1833,9 +1833,11 @@ void VL_FCLOSE_I(IData fdi) VL_MT_SAFE { Verilated::threadContextp()->impp()->fdClose(fdi); } +//=========================================================================== +// String formatting functions taking const std::string& as format string + void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { static thread_local std::string t_output; // static only for speed - t_output = ""; va_list ap; va_start(ap, argc); _vl_vsformat(t_output, format, argc, ap); @@ -1846,7 +1848,6 @@ void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc, void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { static thread_local std::string t_output; // static only for speed - t_output = ""; va_list ap; va_start(ap, argc); _vl_vsformat(t_output, format, argc, ap); @@ -1857,7 +1858,6 @@ void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc, void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { static thread_local std::string t_output; // static only for speed - t_output = ""; va_list ap; va_start(ap, argc); _vl_vsformat(t_output, format, argc, ap); @@ -1868,7 +1868,6 @@ void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, int argc, void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { static thread_local std::string t_output; // static only for speed - t_output = ""; va_list ap; va_start(ap, argc); _vl_vsformat(t_output, format, argc, ap); @@ -1879,7 +1878,6 @@ void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, void VL_SFORMAT_NX(int obits, EData* destp, const std::string& format, int argc, ...) VL_MT_SAFE { static thread_local std::string t_output; // static only for speed - t_output = ""; va_list ap; va_start(ap, argc); _vl_vsformat(t_output, format, argc, ap); @@ -1898,19 +1896,17 @@ void VL_SFORMAT_NX(std::string& output, const std::string& format, int argc, ... } std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE { - static thread_local std::string t_output; // static only for speed - t_output = ""; + std::string output; va_list ap; va_start(ap, argc); - _vl_vsformat(t_output, format, argc, ap); + _vl_vsformat(output, format, argc, ap); va_end(ap); - return t_output; + return output; } void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE { static thread_local std::string t_output; // static only for speed - t_output = ""; va_list ap; va_start(ap, argc); _vl_vsformat(t_output, format, argc, ap); @@ -1922,8 +1918,6 @@ void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE { void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE { // While threadsafe, each thread can only access different file handles static thread_local std::string t_output; // static only for speed - t_output = ""; - va_list ap; va_start(ap, argc); _vl_vsformat(t_output, format, argc, ap); @@ -1932,6 +1926,99 @@ void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SA Verilated::threadContextp()->impp()->fdWrite(fpi, t_output); } +//=========================================================================== +// String formatting functions taking const char* as format string + +void VL_SFORMAT_NX(int obits, CData& destr, const char* formatp, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, formatp, argc, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, SData& destr, const char* formatp, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, formatp, argc, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, IData& destr, const char* formatp, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, formatp, argc, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, QData& destr, const char* formatp, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, formatp, argc, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, EData* destp, const char* formatp, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, formatp, argc, ap); + va_end(ap); + + _vl_string_to_vint(obits, destp, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(std::string& output, const char* formatp, int argc, ...) VL_MT_SAFE { + std::string temp_output; + va_list ap; + va_start(ap, argc); + _vl_vsformat(temp_output, formatp, argc, ap); + va_end(ap); + output = temp_output; +} + +std::string VL_SFORMATF_N_NX(const char* formatp, int argc, ...) VL_MT_SAFE { + std::string output; + va_list ap; + va_start(ap, argc); + _vl_vsformat(output, formatp, argc, ap); + va_end(ap); + + return output; +} + +void VL_WRITEF_NX(const char* formatp, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, formatp, argc, ap); + va_end(ap); + + VL_PRINTF_MT("%s", t_output.c_str()); +} + +void VL_FWRITEF_NX(IData fpi, const char* formatp, int argc, ...) VL_MT_SAFE { + // While threadsafe, each thread can only access different file handles + static thread_local std::string t_output; // static only for speed + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, formatp, argc, ap); + va_end(ap); + + Verilated::threadContextp()->impp()->fdWrite(fpi, t_output); +} + IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE { // While threadsafe, each thread can only access different file handles FILE* const fp = VL_CVT_I_FP(fpi); diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 0fefe9e25..5a1e2b442 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -150,9 +150,6 @@ extern void VL_FCLOSE_I(IData fdi) VL_MT_SAFE; extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, IData start, IData count) VL_MT_SAFE; -extern void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE; -extern void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE; - extern IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE; extern IData VL_SSCANF_IINX(int lbits, IData ld, const std::string& format, int argc, ...) VL_MT_SAFE; @@ -161,6 +158,7 @@ extern IData VL_SSCANF_IQNX(int lbits, QData ld, const std::string& format, int extern IData VL_SSCANF_IWNX(int lbits, WDataInP const lwp, const std::string& format, int argc, ...) VL_MT_SAFE; +// String formatting functions taking const std::string& as format string extern void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc, ...) VL_MT_SAFE; extern void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc, @@ -171,6 +169,22 @@ extern void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, in ...) VL_MT_SAFE; extern void VL_SFORMAT_NX(int obits, EData* destp, const std::string& format, int argc, ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(std::string& output, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE; +extern void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE; +extern void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE; + +// String formatting functions taking const char* format string +extern void VL_SFORMAT_NX(int obits, CData& destr, const char* formatp, int argc, ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, SData& destr, const char* formatp, int argc, ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, IData& destr, const char* formatp, int argc, ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, QData& destr, const char* formatp, int argc, ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, EData* destp, const char* formatp, int argc, ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(std::string& output, const char* formatp, int argc, ...) VL_MT_SAFE; +extern std::string VL_SFORMATF_N_NX(const char* formatp, int argc, ...) VL_MT_SAFE; +extern void VL_WRITEF_NX(const char* formatp, int argc, ...) VL_MT_SAFE; +extern void VL_FWRITEF_NX(IData fpi, const char* formatp, int argc, ...) VL_MT_SAFE; extern void VL_STACKTRACE() VL_MT_SAFE; extern std::string VL_STACKTRACE_N() VL_MT_SAFE; @@ -3668,9 +3682,6 @@ extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb, QData end) VL_MT_SAFE; extern IData VL_SSCANF_INNX(int lbits, const std::string& ld, const std::string& format, int argc, ...) VL_MT_SAFE; -extern void VL_SFORMAT_NX(std::string& output, const std::string& format, int argc, - ...) VL_MT_SAFE; -extern std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE; extern void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision, bool hasSuffix, const std::string& suffix, bool hasWidth, int width, VerilatedContext* contextp) VL_MT_SAFE;