From 7294d8b5af3d94c919271199edf1285ff335ccb3 Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 8 May 2024 00:27:28 +0000 Subject: [PATCH 1/5] cxxrtl: fix close of invalid fd in spool destructor. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h index b8233b007c6..9aa3e105d2c 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h @@ -512,9 +512,10 @@ class spool { spool &operator=(const spool &) = delete; ~spool() { - if (int fd = writefd.exchange(-1)) + int fd; + if ((fd = writefd.exchange(-1)) != -1) close(fd); - if (int fd = readfd.exchange(-1)) + if ((fd = readfd.exchange(-1)) != -1) close(fd); } From 43ddd89ba539e41459e3e83bcb088f8e2e575f8f Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 8 May 2024 02:53:36 +0000 Subject: [PATCH 2/5] cxxrtl: fix `escape_c_string` hex literal fiasco. In C and C++, a `\x` escape sequence consumes as many hexadecimal digits as there are available, so it is not composable with arbitrary alnum characters afterwards. An octal escape sequence like `\000` always has fixed width, avoiding an issue where `\x01c` and `\x1c` produce the same string. --- backends/cxxrtl/cxxrtl_backend.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 877a829d37a..86fe9e81b39 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -615,10 +615,11 @@ std::string escape_cxx_string(const std::string &input) output.push_back('\\'); output.push_back(c); } else { - char l = c & 0xf, h = (c >> 4) & 0xf; - output.append("\\x"); - output.push_back((h < 10 ? '0' + h : 'a' + h - 10)); - output.push_back((l < 10 ? '0' + l : 'a' + l - 10)); + char l = c & 0x3, m = (c >> 3) & 0x3, h = (c >> 6) & 0x3; + output.append("\\"); + output.push_back('0' + h); + output.push_back('0' + m); + output.push_back('0' + l); } } output.push_back('"'); From 9134cd1928a0a043f241fb6e3deef5e0a59d1908 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 21 Mar 2024 20:28:32 +0000 Subject: [PATCH 3/5] cxxrtl: reduce stack space consumed by `debug_info()`. Before this commit, the creation of (constant) attribute maps caused `debug_info()` (which is built with `__attribute__((optnone))`) to consume large amounts of stack space; up to tens of megabytes. This caused problems particularly on macOS, where the default stack size is 512 KiB. After this commit, `std::map` objects are no longer created inline in the `debug_info()` function, but are compiled to and then expanded from a string literal in a subroutine call. This reduces stack space usage by about 50%. --- backends/cxxrtl/cxxrtl_backend.cc | 81 ++++++++++++++++++++----- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 49 +++++++++++++++ 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 86fe9e81b39..42b379637c9 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -606,9 +606,10 @@ std::vector split_by(const std::string &str, const std::string &sep return result; } -std::string escape_cxx_string(const std::string &input) +std::string escape_c_string(const std::string &input) { - std::string output = "\""; + std::string output; + output.push_back('"'); for (auto c : input) { if (::isprint(c)) { if (c == '\\') @@ -623,6 +624,12 @@ std::string escape_cxx_string(const std::string &input) } } output.push_back('"'); + return output; +} + +std::string escape_cxx_string(const std::string &input) +{ + std::string output = escape_c_string(input); if (output.find('\0') != std::string::npos) { output.insert(0, "std::string {"); output.append(stringf(", %zu}", input.size())); @@ -2276,14 +2283,23 @@ struct CxxrtlWorker { dec_indent(); } - void dump_metadata_map(const dict &metadata_map) + void dump_metadata_map(const dict &metadata_map, bool serialize = true) { if (metadata_map.empty()) { f << "metadata_map()"; return; - } - f << "metadata_map({\n"; - inc_indent(); + } else if (serialize) { + // Creating thousands metadata_map objects using initializer lists in a single function results in one of: + // 1. Megabytes of stack usage (with __attribute__((optnone))). + // 2. Minutes of compile time (without __attribute__((optnone))). + // So, don't create them. + std::string data; + auto put_u64 = [&](uint64_t value) { + for (size_t count = 0; count < 8; count++) { + data += (char)(value >> 56); + value <<= 8; + } + }; for (auto metadata_item : metadata_map) { if (!metadata_item.first.isPublic()) continue; @@ -2291,21 +2307,58 @@ struct CxxrtlWorker { f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n"; continue; } - f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", "; + data += metadata_item.first.str().substr(1) + '\0'; // In Yosys, a real is a type of string. if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { - f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint; + double dvalue = std::stod(metadata_item.second.decode_string()); + uint64_t uvalue; + static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size"); + memcpy(&uvalue, &dvalue, sizeof(uvalue)); + data += 'd'; + put_u64(uvalue); } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { - f << escape_cxx_string(metadata_item.second.decode_string()); + data += 's'; + data += metadata_item.second.decode_string(); + data += '\0'; } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) { - f << "INT64_C(" << metadata_item.second.as_int(/*is_signed=*/true) << ")"; + data += 'i'; + put_u64((uint64_t)metadata_item.second.as_int(/*is_signed=*/true)); } else { - f << "UINT64_C(" << metadata_item.second.as_int(/*is_signed=*/false) << ")"; + data += 'u'; + put_u64(metadata_item.second.as_int(/*is_signed=*/false)); } - f << " },\n"; } - dec_indent(); - f << indent << "})"; + f << "metadata::deserialize(\n"; + inc_indent(); + f << indent << escape_c_string(data) << "\n"; + dec_indent(); + f << indent << ")"; + } else { + f << "metadata_map({\n"; + inc_indent(); + for (auto metadata_item : metadata_map) { + if (!metadata_item.first.isPublic()) + continue; + if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) { + f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n"; + continue; + } + f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", "; + // In Yosys, a real is a type of string. + if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { + f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint; + } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { + f << escape_cxx_string(metadata_item.second.decode_string()); + } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) { + f << "INT64_C(" << metadata_item.second.as_int(/*is_signed=*/true) << ")"; + } else { + f << "UINT64_C(" << metadata_item.second.as_int(/*is_signed=*/false) << ")"; + } + f << " },\n"; + } + dec_indent(); + f << indent << "})"; + } } void dump_debug_attrs(const RTLIL::AttrObject *object) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 8546a841189..068dc350535 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -941,6 +941,55 @@ struct metadata { assert(value_type == DOUBLE); return double_value; } + + // Internal CXXRTL use only. + static std::map deserialize(const char *ptr) { + std::map result; + std::string name; + // Grammar: + // string ::= [^\0]+ \0 + // metadata ::= [uid] .{8} | s + // map ::= ( )* \0 + for (;;) { + if (*ptr) { + name += *ptr++; + } else if (!name.empty()) { + ptr++; + auto get_u64 = [&]() { + uint64_t result = 0; + for (size_t count = 0; count < 8; count++) + result = (result << 8) | *ptr++; + return result; + }; + char type = *ptr++; + if (type == 'u') { + uint64_t value = get_u64(); + result.emplace(name, value); + } else if (type == 'i') { + int64_t value = (int64_t)get_u64(); + result.emplace(name, value); + } else if (type == 'd') { + double dvalue; + uint64_t uvalue = get_u64(); + static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size"); + memcpy(&dvalue, &uvalue, sizeof(dvalue)); + result.emplace(name, dvalue); + } else if (type == 's') { + std::string value; + while (*ptr) + value += *ptr++; + ptr++; + result.emplace(name, value); + } else { + assert(false && "Unknown type specifier"); + return result; + } + name.clear(); + } else { + return result; + } + } + } }; typedef std::map metadata_map; From 80798daf53e74dfc02c49bb31f99bd4b01c6537d Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 8 May 2024 01:16:56 +0000 Subject: [PATCH 4/5] cxxrtl: reduce stack space consumed by `debug_info()` further. Before this commit, this function would create a temporary `std::string` per debug item (and scope). After this commit, an additional overload is used to push that down the call stack. This reduces stack usage by about 50% more on top of the previous commit. --- backends/cxxrtl/cxxrtl_backend.cc | 120 ++++++++++++------------ backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 10 ++ 2 files changed, 70 insertions(+), 60 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 42b379637c9..d839bd86709 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2283,56 +2283,52 @@ struct CxxrtlWorker { dec_indent(); } - void dump_metadata_map(const dict &metadata_map, bool serialize = true) - { + void dump_serialized_metadata(const dict &metadata_map) { + // Creating thousands metadata_map objects using initializer lists in a single function results in one of: + // 1. Megabytes of stack usage (with __attribute__((optnone))). + // 2. Minutes of compile time (without __attribute__((optnone))). + // So, don't create them. + std::string data; + auto put_u64 = [&](uint64_t value) { + for (size_t count = 0; count < 8; count++) { + data += (char)(value >> 56); + value <<= 8; + } + }; + for (auto metadata_item : metadata_map) { + if (!metadata_item.first.isPublic()) + continue; + if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) { + f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n"; + continue; + } + data += metadata_item.first.str().substr(1) + '\0'; + // In Yosys, a real is a type of string. + if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { + double dvalue = std::stod(metadata_item.second.decode_string()); + uint64_t uvalue; + static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size"); + memcpy(&uvalue, &dvalue, sizeof(uvalue)); + data += 'd'; + put_u64(uvalue); + } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { + data += 's'; + data += metadata_item.second.decode_string(); + data += '\0'; + } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) { + data += 'i'; + put_u64((uint64_t)metadata_item.second.as_int(/*is_signed=*/true)); + } else { + data += 'u'; + put_u64(metadata_item.second.as_int(/*is_signed=*/false)); + } + } + f << escape_c_string(data); + } + + void dump_metadata_map(const dict &metadata_map) { if (metadata_map.empty()) { f << "metadata_map()"; - return; - } else if (serialize) { - // Creating thousands metadata_map objects using initializer lists in a single function results in one of: - // 1. Megabytes of stack usage (with __attribute__((optnone))). - // 2. Minutes of compile time (without __attribute__((optnone))). - // So, don't create them. - std::string data; - auto put_u64 = [&](uint64_t value) { - for (size_t count = 0; count < 8; count++) { - data += (char)(value >> 56); - value <<= 8; - } - }; - for (auto metadata_item : metadata_map) { - if (!metadata_item.first.isPublic()) - continue; - if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) { - f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n"; - continue; - } - data += metadata_item.first.str().substr(1) + '\0'; - // In Yosys, a real is a type of string. - if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { - double dvalue = std::stod(metadata_item.second.decode_string()); - uint64_t uvalue; - static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size"); - memcpy(&uvalue, &dvalue, sizeof(uvalue)); - data += 'd'; - put_u64(uvalue); - } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { - data += 's'; - data += metadata_item.second.decode_string(); - data += '\0'; - } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) { - data += 'i'; - put_u64((uint64_t)metadata_item.second.as_int(/*is_signed=*/true)); - } else { - data += 'u'; - put_u64(metadata_item.second.as_int(/*is_signed=*/false)); - } - } - f << "metadata::deserialize(\n"; - inc_indent(); - f << indent << escape_c_string(data) << "\n"; - dec_indent(); - f << indent << ")"; } else { f << "metadata_map({\n"; inc_indent(); @@ -2361,14 +2357,18 @@ struct CxxrtlWorker { } } - void dump_debug_attrs(const RTLIL::AttrObject *object) + void dump_debug_attrs(const RTLIL::AttrObject *object, bool serialize = true) { dict attributes = object->attributes; // Inherently necessary to get access to the object, so a waste of space to emit. attributes.erase(ID::hdlname); // Internal Yosys attribute that should be removed but isn't. attributes.erase(ID::module_not_derived); - dump_metadata_map(attributes); + if (serialize) { + dump_serialized_metadata(attributes); + } else { + dump_metadata_map(attributes); + } } void dump_debug_info_method(RTLIL::Module *module) @@ -2391,7 +2391,7 @@ struct CxxrtlWorker { // The module is responsible for adding its own scope. f << indent << "scopes->add(path.empty() ? path : path.substr(0, path.size() - 1), "; f << escape_cxx_string(get_hdl_name(module)) << ", "; - dump_debug_attrs(module); + dump_debug_attrs(module, /*serialize=*/false); f << ", std::move(cell_attrs));\n"; count_scopes++; // If there were any submodules that were flattened, the module is also responsible for adding them. @@ -2401,11 +2401,11 @@ struct CxxrtlWorker { auto module_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Module); auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell); cell_attrs.erase(ID::module_not_derived); - f << indent << "scopes->add(path + " << escape_cxx_string(get_hdl_name(cell)) << ", "; + f << indent << "scopes->add(path, " << escape_cxx_string(get_hdl_name(cell)) << ", "; f << escape_cxx_string(cell->get_string_attribute(ID(module))) << ", "; - dump_metadata_map(module_attrs); + dump_serialized_metadata(module_attrs); f << ", "; - dump_metadata_map(cell_attrs); + dump_serialized_metadata(cell_attrs); f << ");\n"; } else log_assert(false && "Unknown $scopeinfo type"); count_scopes++; @@ -2473,7 +2473,7 @@ struct CxxrtlWorker { if (has_driven_sync + has_driven_comb + has_undriven > 1) count_mixed_driver++; - f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset; bool first = true; for (auto flag : flags) { @@ -2494,7 +2494,7 @@ struct CxxrtlWorker { case WireType::ALIAS: { // Alias of a member wire const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire(); - f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item("; // If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream // tooling has no way to find out about the outline. @@ -2513,7 +2513,7 @@ struct CxxrtlWorker { f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = "; dump_const(debug_wire_type.sig_subst.as_const()); f << ";\n"; - f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), "; dump_debug_attrs(wire); f << ");\n"; @@ -2522,7 +2522,7 @@ struct CxxrtlWorker { } case WireType::OUTLINE: { // Localized or inlined, but rematerializable wire - f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), "; dump_debug_attrs(wire); f << ");\n"; @@ -2540,7 +2540,7 @@ struct CxxrtlWorker { for (auto &mem : mod_memories[module]) { if (!mem.memid.isPublic()) continue; - f << indent << "items->add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); + f << indent << "items->add(path, " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); f << ", debug_item(" << mangle(&mem) << ", "; f << mem.start_offset << "), "; if (mem.packed) { @@ -2560,7 +2560,7 @@ struct CxxrtlWorker { const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; f << indent << mangle(cell) << access; f << "debug_info(items, scopes, path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ", "; - dump_debug_attrs(cell); + dump_debug_attrs(cell, /*serialize=*/false); f << ");\n"; } } diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 068dc350535..8f47bc18990 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1466,6 +1466,11 @@ struct debug_items { }); } + // This overload exists to reduce excessive stack slot allocation in `CXXRTL_EXTREMELY_COLD void debug_info()`. + void add(const std::string &base_path, const char *path, debug_item &&item, const char *serialized_item_attrs) { + add(base_path + path, std::move(item), metadata::deserialize(serialized_item_attrs)); + } + size_t count(const std::string &path) const { if (table.count(path) == 0) return 0; @@ -1512,6 +1517,11 @@ struct debug_scopes { scope.cell_attrs = std::unique_ptr(new debug_attrs { cell_attrs }); } + // This overload exists to reduce excessive stack slot allocation in `CXXRTL_EXTREMELY_COLD void debug_info()`. + void add(const std::string &base_path, const char *path, const char *module_name, const char *serialized_module_attrs, const char *serialized_cell_attrs) { + add(base_path + path, module_name, metadata::deserialize(serialized_module_attrs), metadata::deserialize(serialized_cell_attrs)); + } + size_t contains(const std::string &path) const { return table.count(path); } From 6e003e1af67ce4709c63812f6e1d1732ada9ba15 Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 8 May 2024 03:37:14 +0000 Subject: [PATCH 5/5] cxxrtl: minimize stack space consumed by `debug_info()`. This commit uses parameter packs to sink `debug_item()` construction into the `debug_info()`-specific `add()` overload. This makes the stack space use sub-linear in typical case rather than linear (which is still the worst case). Oddly, the stack slots that get allocated now are all for the `0` literal for `lsb_offset`. This could be fixed by allocating numbers statically but the existing reduction in stack use of ~98% for a representative example (Minerva SoC) should be enough. --- backends/cxxrtl/cxxrtl_backend.cc | 55 ++++++++++++++----------- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 5 ++- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index d839bd86709..703494682a8 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2473,20 +2473,22 @@ struct CxxrtlWorker { if (has_driven_sync + has_driven_comb + has_undriven > 1) count_mixed_driver++; - f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); - f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset; - bool first = true; - for (auto flag : flags) { - if (first) { - first = false; - f << ", "; - } else { - f << "|"; + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", "; + dump_debug_attrs(wire); + f << ", " << mangle(wire); + if (wire->start_offset != 0 || !flags.empty()) { + f << ", " << wire->start_offset; + bool first = true; + for (auto flag : flags) { + if (first) { + first = false; + f << ", "; + } else { + f << "|"; + } + f << "debug_item::" << flag; } - f << "debug_item::" << flag; } - f << "), "; - dump_debug_attrs(wire); f << ");\n"; count_member_wires++; break; @@ -2494,16 +2496,18 @@ struct CxxrtlWorker { case WireType::ALIAS: { // Alias of a member wire const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire(); - f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); - f << ", debug_item("; + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", "; + dump_debug_attrs(aliasee); + f << ", "; // If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream // tooling has no way to find out about the outline. if (debug_wire_types[aliasee].is_outline()) f << "debug_eval_outline"; else f << "debug_alias()"; - f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), "; - dump_debug_attrs(aliasee); + f << ", " << mangle(aliasee); + if (wire->start_offset != 0) + f << ", " << wire->start_offset; f << ");\n"; count_alias_wires++; break; @@ -2513,18 +2517,22 @@ struct CxxrtlWorker { f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = "; dump_const(debug_wire_type.sig_subst.as_const()); f << ";\n"; - f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); - f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), "; + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", "; dump_debug_attrs(wire); + f << ", const_" << mangle(wire); + if (wire->start_offset != 0) + f << ", " << wire->start_offset; f << ");\n"; count_const_wires++; break; } case WireType::OUTLINE: { // Localized or inlined, but rematerializable wire - f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)); - f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), "; + f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", "; dump_debug_attrs(wire); + f << ", debug_eval_outline, " << mangle(wire); + if (wire->start_offset != 0) + f << ", " << wire->start_offset; f << ");\n"; count_inline_wires++; break; @@ -2540,15 +2548,14 @@ struct CxxrtlWorker { for (auto &mem : mod_memories[module]) { if (!mem.memid.isPublic()) continue; - f << indent << "items->add(path, " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); - f << ", debug_item(" << mangle(&mem) << ", "; - f << mem.start_offset << "), "; + f << indent << "items->add(path, " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)) << ", "; if (mem.packed) { dump_debug_attrs(mem.cell); } else { dump_debug_attrs(mem.mem); } - f << ");\n"; + f << ", " << mangle(&mem) << ", "; + f << mem.start_offset << ");\n"; } } dec_indent(); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 8f47bc18990..057d34cf906 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1467,8 +1467,9 @@ struct debug_items { } // This overload exists to reduce excessive stack slot allocation in `CXXRTL_EXTREMELY_COLD void debug_info()`. - void add(const std::string &base_path, const char *path, debug_item &&item, const char *serialized_item_attrs) { - add(base_path + path, std::move(item), metadata::deserialize(serialized_item_attrs)); + template + void add(const std::string &base_path, const char *path, const char *serialized_item_attrs, T&&... args) { + add(base_path + path, debug_item(std::forward(args)...), metadata::deserialize(serialized_item_attrs)); } size_t count(const std::string &path) const {