diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 5191da5b8..07771ba9e 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -79,6 +79,7 @@ struct BtorWorker dict initbits; pool statewires; pool srcsymbols; + TwineSearch src_search; vector memories; dict mem_cells; @@ -125,11 +126,11 @@ struct BtorWorker string src = module && module->design ? module->design->get_src_attribute(obj) : std::string(); if (srcsym && infostr[0] == '$') { std::replace(src.begin(), src.end(), ' ', '_'); - TwineRef src_ref = TwineSearch(&module->design->twines).find(src); + TwineRef src_ref = src_search.find(src); if (srcsymbols.count(src) || src_ref != Twine::Null) { for (int i = 1;; i++) { string s = stringf("%s-%d", src, i); - TwineRef s_ref = TwineSearch(&module->design->twines).find(s); + TwineRef s_ref = src_search.find(s); if (!srcsymbols.count(s) && s_ref == Twine::Null) { src = s; break; @@ -153,11 +154,11 @@ struct BtorWorker string src = module && module->design ? module->design->get_src_attribute(mem) : std::string(); if (srcsym && infostr[0] == '$') { std::replace(src.begin(), src.end(), ' ', '_'); - TwineRef src_ref = TwineSearch(&module->design->twines).find(src); + TwineRef src_ref = src_search.find(src); if (srcsymbols.count(src) || src_ref != Twine::Null) { for (int i = 1;; i++) { string s = stringf("%s-%d", src, i); - TwineRef s_ref = TwineSearch(&module->design->twines).find(s); + TwineRef s_ref = src_search.find(s); if (!srcsymbols.count(s) && s_ref == Twine::Null) { src = s; break; @@ -1193,7 +1194,7 @@ struct BtorWorker } BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, bool print_internal_names, string info_filename, string ywmap_filename) : - f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), info_filename(info_filename) + f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), src_search(&module->design->twines), info_filename(info_filename) { if (!info_filename.empty()) infof("name %s\n", module); diff --git a/backends/json/json.cc b/backends/json/json.cc index a1d10b4db..589a5ab00 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -138,8 +138,6 @@ struct JsonWriter void write_parameters(const dict ¶meters, bool for_module=false, const RTLIL::AttrObject *src_obj=nullptr) { bool first = true; - // Emit the typed src field first if present — it lives outside the - // attribute dict after the typed-src migration. if (src_obj && design && design->obj_src_id(src_obj) != Twine::Null) { f << stringf("\n %s%s: ", for_module ? "" : " ", get_name(RTLIL::ID::src)); write_parameter_value(RTLIL::Const(design->get_src_attribute(src_obj))); diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index 3030ce074..42c17aa9b 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -34,9 +34,6 @@ YOSYS_NAMESPACE_BEGIN void RTLIL_BACKEND::dump_attributes(std::ostream &f, std::string indent, const RTLIL::AttrObject *obj, const RTLIL::Design *design, bool stringify) { - // Emit the typed src field first. It is not stored in obj->attributes - // — the dict no longer holds ID::src under any circumstance. Backends - // that want to materialize the pipe-joined literal pass stringify. if (design && design->obj_src_id(obj) != Twine::Null) { TwineRef id = design->obj_src_id(obj); f << stringf("%s" "attribute \\src ", indent); diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index b2d0663af..f031cb805 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -269,13 +269,14 @@ end_of_header: else log_abort(); - RTLIL::Wire* n0 = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$0", aiger_autoidx))); + RTLIL::Wire* n0 = aiger_wires.count(0) ? aiger_wires.at(0) : nullptr; if (n0) module->connect(n0, State::S0); // Parse footer (symbol table, comments, etc.) unsigned l1; std::string s; + TwineSearch search(&design->twines); for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { if (c == 'i' || c == 'l' || c == 'o' || c == 'b') { f.ignore(1); @@ -294,7 +295,7 @@ end_of_header: log_assert(l1 < latches.size()); wire = latches[l1]; } else if (c == 'o') { - wire = module->wire(TwineSearch(&design->twines).find(escaped_s.str())); + wire = module->wire(search.find(escaped_s.str())); log_assert(l1 < outputs.size()); if (wire) { // Could have been renamed by a latch @@ -307,7 +308,7 @@ end_of_header: wire = bad_properties[l1]; } else log_abort(); - module->rename(wire, design->twines.add(std::string{escaped_s.str()})); + module->rename(wire, intern_name(escaped_s.str(), search)); } else if (c == 'j' || c == 'f') { // TODO @@ -346,25 +347,28 @@ RTLIL::Wire* AigerReader::createWireIfNotExists(RTLIL::Module *module, unsigned { const unsigned variable = literal >> 1; const bool invert = literal & 1; + if (auto it = aiger_wires.find(literal); it != aiger_wires.end()) + return it->second; RTLIL::IdString wire_name(stringf("$aiger%d$%d%s", aiger_autoidx, variable, invert ? "b" : "")); - RTLIL::Wire *wire = module->wire(TwineSearch(&design->twines).find(wire_name.str())); - if (wire) return wire; log_debug2("Creating %s\n", wire_name.c_str()); - wire = module->addWire(design->twines.add(std::string{wire_name.str()})); + RTLIL::Wire *wire = module->addWire(design->twines.add(std::string{wire_name.str()})); wire->port_input = wire->port_output = false; + aiger_wires[literal] = wire; if (!invert) return wire; - RTLIL::IdString wire_inv_name(stringf("$aiger%d$%d", aiger_autoidx, variable)); - RTLIL::Wire *wire_inv = module->wire(TwineSearch(&design->twines).find(wire_inv_name.str())); - if (wire_inv) { - if (module->cell(TwineSearch(&design->twines).find(wire_inv_name.str()))) return wire; + const unsigned base_literal = variable << 1; + RTLIL::Wire *wire_inv; + if (auto it = aiger_wires.find(base_literal); it != aiger_wires.end()) { + wire_inv = it->second; } else { + RTLIL::IdString wire_inv_name(stringf("$aiger%d$%d", aiger_autoidx, variable)); log_debug2("Creating %s\n", wire_inv_name.c_str()); wire_inv = module->addWire(design->twines.add(std::string{wire_inv_name.str()})); wire_inv->port_input = wire_inv->port_output = false; + aiger_wires[base_literal] = wire_inv; } - log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); + log_debug2("Creating %s = ~$aiger%d$%d\n", wire_name.c_str(), aiger_autoidx, variable); module->addNotGate(Twine{stringf("$not$aiger%d$%d", aiger_autoidx, variable)}, wire_inv, wire); return wire; @@ -402,7 +406,7 @@ void AigerReader::parse_xaiger() else log_abort(); - RTLIL::Wire* n0 = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$0", aiger_autoidx))); + RTLIL::Wire* n0 = aiger_wires.count(0) ? aiger_wires.at(0) : nullptr; if (n0) module->connect(n0, State::S0); @@ -422,11 +426,12 @@ void AigerReader::parse_xaiger() uint32_t lutSize = parse_xaiger_literal(f); log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); ConstEvalAig ce(module); + TwineSearch search(&design->twines); for (unsigned i = 0; i < lutNum; ++i) { uint32_t rootNodeID = parse_xaiger_literal(f); uint32_t cutLeavesM = parse_xaiger_literal(f); log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM); - RTLIL::Wire *output_sig = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID))); + RTLIL::Wire *output_sig = module->wire(search.find(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID))); log_assert(output_sig); uint32_t nodeID; RTLIL::SigSpec input_sig; @@ -437,7 +442,7 @@ void AigerReader::parse_xaiger() log_debug("\tLUT '$lut$aiger%d$%d' input %d is constant!\n", aiger_autoidx, rootNodeID, cutLeavesM); continue; } - RTLIL::Wire *wire = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$%d", aiger_autoidx, nodeID))); + RTLIL::Wire *wire = module->wire(search.find(stringf("$aiger%d$%d", aiger_autoidx, nodeID))); log_assert(wire); input_sig.append(wire); } @@ -456,7 +461,7 @@ void AigerReader::parse_xaiger() log_assert(o.wire == nullptr); lut_mask.set(gray, o.data); } - RTLIL::Cell *output_cell = module->cell(TwineSearch(&design->twines).find(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID))); + RTLIL::Cell *output_cell = module->cell(search.find(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID))); log_assert(output_cell); module->remove(output_cell); module->addLut(Twine{stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID)}, input_sig, output_sig, std::move(lut_mask)); @@ -772,6 +777,21 @@ void AigerReader::parse_aiger_binary() } } +// add(std::string) always interns a fresh leaf, but an equal-content name may +// already exist in the pool as a NEW_TWINE suffix (auto-generated names share a +// prefix leaf). Reuse that ref so every wire/cell with the same name string is +// keyed by a single canonical ref; otherwise leaf-vs-suffix refs diverge and +// module->wire()/cell() lookups miss. +TwineRef AigerReader::intern_name(const std::string &escaped, TwineSearch &search) +{ + TwineRef existing = search.find(escaped); + if (existing != Twine::Null) + return existing; + TwineRef ref = design->twines.add(std::string{escaped}); + search.insert(ref); + return ref; +} + void AigerReader::post_process() { unsigned ci_count = 0, co_count = 0; @@ -816,6 +836,7 @@ void AigerReader::post_process() std::ifstream mf(map_filename); std::string type, symbol; int variable, index; + TwineSearch search(&design->twines); while (mf >> type >> variable >> index >> symbol) { RTLIL::IdString escaped_s = RTLIL::escape_id(symbol); if (type == "input") { @@ -830,9 +851,9 @@ void AigerReader::post_process() // Cope with the fact that a CI might be identical // to a PI (necessary due to ABC); in those cases // simply connect the latter to the former - existing = module->wire(TwineSearch(&design->twines).find(escaped_s.str())); + existing = module->wire(search.find(escaped_s.str())); if (!existing) - module->rename(wire, design->twines.add(std::string{escaped_s.str()})); + module->rename(wire, intern_name(escaped_s.str(), search)); else { wire->port_input = false; module->connect(wire, existing); @@ -841,9 +862,9 @@ void AigerReader::post_process() } else { RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index); - existing = module->wire(TwineSearch(&design->twines).find(indexed_name.str())); + existing = module->wire(search.find(indexed_name.str())); if (!existing) - module->rename(wire, design->twines.add(std::string{indexed_name.str()})); + module->rename(wire, intern_name(indexed_name.str(), search)); else { module->connect(wire, existing); wire->port_input = false; @@ -875,9 +896,9 @@ void AigerReader::post_process() // Cope with the fact that a CO might be identical // to a PO (necessary due to ABC); in those cases // simply connect the latter to the former - existing = module->wire(TwineSearch(&design->twines).find(escaped_s.str())); + existing = module->wire(search.find(escaped_s.str())); if (!existing) - module->rename(wire, design->twines.add(std::string{escaped_s.str()})); + module->rename(wire, intern_name(escaped_s.str(), search)); else { wire->port_output = false; existing->port_output = true; @@ -888,9 +909,9 @@ void AigerReader::post_process() } else { RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index); - existing = module->wire(TwineSearch(&design->twines).find(indexed_name.str())); + existing = module->wire(search.find(indexed_name.str())); if (!existing) - module->rename(wire, design->twines.add(std::string{indexed_name.str()})); + module->rename(wire, intern_name(indexed_name.str(), search)); else { wire->port_output = false; existing->port_output = true; @@ -912,17 +933,18 @@ void AigerReader::post_process() } } else if (type == "box") { - RTLIL::Cell* cell = module->cell(TwineSearch(&design->twines).find(stringf("$box%d", variable))); + RTLIL::Cell* cell = module->cell(search.find(stringf("$box%d", variable))); if (!cell) log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s)); else - module->rename(cell, design->twines.add(std::string{escaped_s.str()})); + module->rename(cell, intern_name(escaped_s.str(), search)); } else log_error("Symbol type '%s' not recognised.\n", type); } } + TwineSearch wp_search(&design->twines); for (auto &wp : wideports_cache) { auto name = wp.first; int min = wp.second.first; @@ -930,30 +952,33 @@ void AigerReader::post_process() if (min == 0 && max == 0) continue; - RTLIL::Wire *wire = module->wire(TwineSearch(&design->twines).find(name.str())); - if (wire) - module->rename(wire, design->twines.add(std::string{RTLIL::escape_id(stringf("%s[%d]", name.str(), 0))})); + RTLIL::Wire *wire = module->wire(wp_search.find(name.str())); + if (wire) { + TwineRef renamed = design->twines.add(std::string{RTLIL::escape_id(stringf("%s[%d]", name.str(), 0))}); + wp_search.insert(renamed); + module->rename(wire, renamed); + } // Do not make ports with a mix of input/output into // wide ports bool port_input = false, port_output = false; for (int i = min; i <= max; i++) { RTLIL::IdString other_name = name.str() + stringf("[%d]", i); - RTLIL::Wire *other_wire = module->wire(TwineSearch(&design->twines).find(other_name.str())); + RTLIL::Wire *other_wire = module->wire(wp_search.find(other_name.str())); if (other_wire) { port_input = port_input || other_wire->port_input; port_output = port_output || other_wire->port_output; } } - wire = module->addWire(design->twines.add(std::string{name.str()}), max-min+1); + wire = module->addWire(intern_name(name.str(), wp_search), max-min+1); wire->start_offset = min; wire->port_input = port_input; wire->port_output = port_output; for (int i = min; i <= max; i++) { RTLIL::IdString other_name = stringf("%s[%d]", name, i); - RTLIL::Wire *other_wire = module->wire(TwineSearch(&design->twines).find(other_name.str())); + RTLIL::Wire *other_wire = module->wire(wp_search.find(other_name.str())); if (other_wire) { other_wire->port_input = false; other_wire->port_output = false; diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 81b955947..e8097d5af 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -46,6 +46,7 @@ struct AigerReader std::vector bad_properties; std::vector boxes; std::vector mergeability, initial_state; + dict aiger_wires; AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); @@ -54,6 +55,7 @@ struct AigerReader void parse_aiger_binary(); void post_process(); + TwineRef intern_name(const std::string &escaped, TwineSearch &search); RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal); }; diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc index 65791664d..874eed3e5 100644 --- a/frontends/aiger2/xaiger.cc +++ b/frontends/aiger2/xaiger.cc @@ -414,7 +414,7 @@ struct Xaiger2Frontend : public Frontend { log_error("Bad map file: primary output literal out of range\n"); if (bits[lit] == RTLIL::Sm) log_error("Bad map file: primary output literal is a marker\n"); - Wire *w = module->wire(TwineSearch(&design->twines).find(name)); + Wire *w = module->wire(search.find(name)); if (!w || woffset < 0 || woffset >= w->width) log_error("Map file references non-existent signal bit %s[%d]\n", name.c_str(), woffset); @@ -434,8 +434,8 @@ struct Xaiger2Frontend : public Frontend { log_error("Bad map file: pseudo primary output literal out of range\n"); if (bits[lit] == RTLIL::Sm) log_error("Bad map file: pseudo primary output literal is a marker\n"); - Cell *cell = module->cell(TwineSearch(&design->twines).find(box_name)); - auto box_port_ref = TwineSearch(&design->twines).find(box_port); + Cell *cell = module->cell(search.find(box_name)); + auto box_port_ref = search.find(box_port); if (!cell || !cell->hasPort(box_port_ref)) log_error("Map file references non-existent box port %s/%s\n", box_name.c_str(), box_port.c_str()); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 851bdb557..1bb79a2ce 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -1106,10 +1106,6 @@ std::string AstNode::loc_string() const void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) { - // All AttrObjects in genrtlil — Cell/Wire/Module/Process AND the inner - // types (CaseRule, SwitchRule, MemWriteAction) — share current_module's - // design's twine pool. process_module attaches current_module->design - // early so this is reachable. if (!current_module || !current_module->design) return; const auto &loc = ast->location; @@ -1152,11 +1148,6 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d AstModule *module = new AstModule; current_module = module; - // Set design backpointer early — every set_src_attr in genrtlil.cc - // resolves the pool via current_module->design->twines. The - // final design->add(current_module) at end-of-process_module hooks - // the module into the design's modules_ dict; we just need design - // reachable as a backpointer for src interning meanwhile. module->design = design; module->ast = nullptr; diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index dd7a96021..99b1c6568 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -312,7 +312,7 @@ void json_import(Design *design, string &modname, JsonNode *node) Module *module = new RTLIL::Module; module->design = design; - module->meta_->name = design->twines.add(Twine{RTLIL::escape_id(modname)}); + module->meta_->name = design->twines.add(RTLIL::escape_id(modname)); if (design->module(module->meta_->name)) log_error("Re-definition of module %s.\n", design->twines.str(module->meta_->name)); @@ -327,6 +327,8 @@ void json_import(Design *design, string &modname, JsonNode *node) dict signal_bits; + dict wire_cache; + if (node->data_dict.count("ports")) { JsonNode *ports_node = node->data_dict.at("ports"); @@ -357,10 +359,12 @@ void json_import(Design *design, string &modname, JsonNode *node) if (port_bits_node->type != 'A') log_error("JSON port node '%s' has non-array bits attribute.\n", port_name.unescape()); - Wire *port_wire = module->wire(TwineSearch(&design->twines).find(port_name.str())); + Wire *port_wire = wire_cache.count(port_name) ? wire_cache.at(port_name) : nullptr; - if (port_wire == nullptr) - port_wire = module->addWire(Twine{port_name.str()}, GetSize(port_bits_node->data_array)); + if (port_wire == nullptr) { + port_wire = module->addWire(design->twines.add(port_name.str()), GetSize(port_bits_node->data_array)); + wire_cache[port_name] = port_wire; + } if (port_node->data_dict.count("upto") != 0) { JsonNode *val = port_node->data_dict.at("upto"); @@ -455,10 +459,12 @@ void json_import(Design *design, string &modname, JsonNode *node) if (bits_node->type != 'A') log_error("JSON netname node '%s' has non-array bits attribute.\n", net_name.unescape()); - Wire *wire = module->wire(TwineSearch(&design->twines).find(net_name.str())); + Wire *wire = wire_cache.count(net_name) ? wire_cache.at(net_name) : nullptr; - if (wire == nullptr) - wire = module->addWire(Twine{net_name.str()}, GetSize(bits_node->data_array)); + if (wire == nullptr) { + wire = module->addWire(design->twines.add(net_name.str()), GetSize(bits_node->data_array)); + wire_cache[net_name] = wire; + } if (net_node->data_dict.count("upto") != 0) { JsonNode *val = net_node->data_dict.at("upto"); @@ -532,7 +538,7 @@ void json_import(Design *design, string &modname, JsonNode *node) IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str()); - Cell *cell = module->addCell(Twine{cell_name.str()}, Twine{cell_type.str()}); + Cell *cell = module->addCell(design->twines.add(cell_name.str()), design->twines.add(cell_type.str())); if (cell_node->data_dict.count("connections") == 0) log_error("JSON cells node '%s' has no connections attribute.\n", cell_name.unescape()); @@ -580,7 +586,7 @@ void json_import(Design *design, string &modname, JsonNode *node) } - cell->setPort(design->twines.add(Twine{conn_name.str()}), sig); + cell->setPort(design->twines.add(conn_name.str()), sig); } if (cell_node->data_dict.count("attributes")) @@ -604,7 +610,7 @@ void json_import(Design *design, string &modname, JsonNode *node) JsonNode *memory_node = memory_node_it.second; RTLIL::Memory *mem = new RTLIL::Memory; - mem->meta_->name = design->twines.add(Twine{memory_name.str()}); + mem->meta_->name = design->twines.add(memory_name.str()); mem->module = module; if (memory_node->type != 'D') diff --git a/frontends/rtlil/rtlil_frontend.cc b/frontends/rtlil/rtlil_frontend.cc index 1dbb5e3a1..a85ad5b3d 100644 --- a/frontends/rtlil/rtlil_frontend.cc +++ b/frontends/rtlil/rtlil_frontend.cc @@ -50,26 +50,14 @@ struct RTLILFrontendWorker { RTLIL::Module *current_module; dict attrbuf; - // A resolved "@N" src reference, carried from parse_attribute to the - // object's absorb_attrs site so it lands on the object without being - // flattened into a fresh leaf. + TwineRef pending_src = Twine::Null; std::vector*> switch_stack; std::vector case_stack; - // Remap from file-local twine ids (as they appear in the `twines` block - // and on cell/wire src attrs) to ids in design->twines. Filled by - // parse_twines; consumed by parse_attribute. Parser-side ids retained - // during parse_twines are tracked here so they can be released at - // end-of-parse — only the cell/wire references should survive. dict twine_remap; std::vector twine_parser_holds; - // Raw twines-block node descriptors keyed by file-local id. The block may - // list a node before its children (the writer emits in pool-index order, - // and free-list slot reuse can place a parent at a lower index than a - // child), so descriptors are collected first and materialized on demand in - // dependency order via materialize_file_twine. struct TwineDesc { enum Kind { Leaf, Suffix, Concat } kind; std::string text; @@ -481,8 +469,6 @@ struct RTLILFrontendWorker { current_module->design = design; current_module->meta_->name = module_name; if (delete_current_module) { - // Module is about to be discarded — drop its src attribute - // rather than push it into a pool we'll never reach. attrbuf.erase(ID::src); pending_src = Twine::Null; current_module->attributes = std::move(attrbuf); @@ -543,18 +529,9 @@ struct RTLILFrontendWorker { { RTLIL::IdString id = parse_id(); RTLIL::Const c = parse_const(); - // The '|' separator inside a src attribute is a Yosys-internal - // merge convention emitted only by the legacy strpool path or by - // a `dump -resolve-src`; no external tool should be producing it. - // Warn so the producer learns to emit one path:line.col per - // attribute. We don't try to repair the value — the user's input - // is wrong and silently interning it would hide that. if (id == RTLIL::ID::src && (c.flags & RTLIL::CONST_FLAG_STRING)) { std::string raw = c.decode_string(); if (!raw.empty() && raw[0] == '@') { - // Compact "@N" reference into the `twines` block: carry the - // resolved twine straight to the object (set after its - // absorb_attrs) so its structure is preserved, not flattened. size_t file_id = 0; auto [ptr, ec] = std::from_chars(raw.data() + 1, raw.data() + raw.size(), file_id); if (ec != std::errc() || ptr != raw.data() + raw.size()) @@ -591,10 +568,7 @@ struct RTLILFrontendWorker { base = TwineRef(id); } else { auto it = twine_remap.find(id); - if (it == twine_remap.end()) - base = materialize_file_twine(id); - else - base = it->second; + base = (it == twine_remap.end()) ? materialize_file_twine(id) : it->second; } return twine_tag(base, is_public); } @@ -708,21 +682,17 @@ struct RTLILFrontendWorker { error("Expected `leaf`, `suffix` or `concat` inside twines block, got `%s'.", error_token()); } + std::vector ordered_ids; + ordered_ids.reserve(twine_descs.size()); for (auto &it : twine_descs) - materialize_file_twine(it.first); + ordered_ids.push_back(it.first); + std::sort(ordered_ids.begin(), ordered_ids.end()); + for (size_t id : ordered_ids) + materialize_file_twine(id); twine_descs.clear(); expect_eol(); } - // Release the per-file parser refs gathered during parse_twines. Call - // once the entire file has been parsed and every cell/wire that ever - // referred to a file_id has already adopted the corresponding local_id. - void release_twine_parser_holds() - { - twine_parser_holds.clear(); - twine_remap.clear(); - } - void parse_parameter() { RTLIL::IdString id = parse_id(); @@ -1080,9 +1050,6 @@ struct RTLILFrontendWorker { act.enable = parse_sigspec(); act.priority_mask = parse_const(); rule->mem_write_actions.push_back(act); - // meta_idx_ is a weak ref — drop ours so the pushed copy - // in the vector is the sole holder. Process::~Process - // walks the tree to actually release. act.meta_ = nullptr; expect_eol(); } @@ -1126,7 +1093,9 @@ struct RTLILFrontendWorker { } if (attrbuf.size() != 0) error("dangling attribute"); - release_twine_parser_holds(); + + twine_parser_holds.clear(); + twine_remap.clear(); } }; diff --git a/kernel/ff.cc b/kernel/ff.cc index d8abee38a..6065194ed 100644 --- a/kernel/ff.cc +++ b/kernel/ff.cc @@ -39,12 +39,6 @@ void manufacture_info(InputType flop, OutputType& info, FfInitVals *initvals) { info.sig_q = cell->getPort(TW::Q); info.width = GetSize(info.sig_q); info.attributes = cell->attributes; - // Carry src across construction → emit() as an owning Twine - // reference. Retaining a slot on the source pool keeps it - // alive even if the source cell gets removed between - // manufacture_info() and emit(); emit() then transfers the - // id verbatim into the new cell — no flatten/re-intern, no - // pipe-leaf risk for cells whose src is a Concat. if (cell->src_id() != Twine::Null && cell->module && cell->module->design) info.src_twine = cell->src_id(); if (initvals) @@ -762,7 +756,6 @@ Cell *FfData::emit() { } } // src is carried in info.src_twine (an OwnedTwine retaining the - // source slot). Transfer the id verbatim to the new cell — same // pool, no flatten. The OwnedTwine still holds its own ref until // FfData is destroyed; set_src_id retains on the cell's behalf. cell->attributes = attributes; diff --git a/kernel/ff.h b/kernel/ff.h index e3b5c1fc8..e111228c4 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -172,8 +172,9 @@ struct FfData : FfTypeData { dict attributes; // Stashed src across construction → emit. Refcount-managed so the // source cell's pool slot survives if the cell itself is removed - // before emit() runs. Empty when the source cell had no src. - TwineRef src_twine; + // before emit() runs. Null when the source cell had no src (default + // TwineRef() is index 0, a valid constid, so it must be Null here). + TwineRef src_twine = Twine::Null; FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) { width = 0; diff --git a/kernel/mem.cc b/kernel/mem.cc index 77086a3ca..b785ff572 100644 --- a/kernel/mem.cc +++ b/kernel/mem.cc @@ -887,7 +887,6 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) { return nullptr; // Keep src as a "@N" reference into the design's twine pool throughout - // — never flatten to a literal path string. That way every addX call // below adopts the same slot as Mem itself (via set_src_attribute's // "@N" parse_ref path), and there's no flatten → re-intern → pipe- // leaf round-trip on cells whose src is a Concat node. @@ -998,7 +997,6 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) { IdString name = stringf("$%s$rdreg[%d]", memid, idx); FfData ff(module, initvals, name); - // Carry mem's src into the ff via the OwnedTwine handle — same // pool, direct id retain. emit() transfers verbatim. ff.src_twine = mem_src; ff.width = GetSize(port.data); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index ecb784732..eed0cd72c 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -953,7 +953,6 @@ bool RTLIL::AttrObject::get_bool_attribute(IdString id) const void RTLIL::AttrObject::set_string_attribute(IdString id, string value) { // ID::src on the base AttrObject is not routable here because the base - // has no Design context — callers needing string-form src must go // through the subtype helper (Cell::set_src_attribute / Wire::… / …) // which derives the design from context. log_assert(id != ID::src && "set_string_attribute(ID::src,...) on AttrObject base; use the subtype helper"); @@ -965,7 +964,6 @@ void RTLIL::AttrObject::set_string_attribute(IdString id, string value) string RTLIL::AttrObject::get_string_attribute(IdString id) const { - // ID::src is not in the dict — callers must use the subtype helper. log_assert(id != ID::src && "get_string_attribute(ID::src) on AttrObject base; use the subtype helper"); std::string value; const auto it = attributes.find(id); @@ -1821,22 +1819,22 @@ std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartial switch (boxes) { case RTLIL::SB_UNBOXED_WARN: - log_warning("Ignoring boxed module %s.\n", twines.str(it.first).c_str()); + log_warning("Ignoring boxed module %s.\n", log_id(it.second)); break; case RTLIL::SB_EXCL_BB_WARN: - log_warning("Ignoring blackbox module %s.\n", twines.str(it.first).c_str()); + log_warning("Ignoring blackbox module %s.\n", log_id(it.second)); break; case RTLIL::SB_UNBOXED_ERR: - log_error("Unsupported boxed module %s.\n", twines.str(it.first).c_str()); + log_error("Unsupported boxed module %s.\n", log_id(it.second)); break; case RTLIL::SB_EXCL_BB_ERR: - log_error("Unsupported blackbox module %s.\n", twines.str(it.first).c_str()); + log_error("Unsupported blackbox module %s.\n", log_id(it.second)); break; case RTLIL::SB_UNBOXED_CMDERR: - log_cmd_error("Unsupported boxed module %s.\n", twines.str(it.first).c_str()); + log_cmd_error("Unsupported boxed module %s.\n", log_id(it.second)); break; case RTLIL::SB_EXCL_BB_CMDERR: - log_cmd_error("Unsupported blackbox module %s.\n", twines.str(it.first).c_str()); + log_cmd_error("Unsupported blackbox module %s.\n", log_id(it.second)); break; default: break; @@ -1845,13 +1843,13 @@ std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartial switch(partials) { case RTLIL::SELECT_WHOLE_WARN: - log_warning("Ignoring partially selected module %s.\n", twines.str(it.first).c_str()); + log_warning("Ignoring partially selected module %s.\n", log_id(it.second)); break; case RTLIL::SELECT_WHOLE_ERR: - log_error("Unsupported partially selected module %s.\n", twines.str(it.first).c_str()); + log_error("Unsupported partially selected module %s.\n", log_id(it.second)); break; case RTLIL::SELECT_WHOLE_CMDERR: - log_cmd_error("Unsupported partially selected module %s.\n", twines.str(it.first).c_str()); + log_cmd_error("Unsupported partially selected module %s.\n", log_id(it.second)); break; default: break; @@ -1893,7 +1891,6 @@ RTLIL::Module::~Module() delete pr.second; for (auto binding : bindings_) delete binding; - // Module's own src — release last so the pool stays valid for // inner releases above. if (design) design->obj_release_src(this); @@ -3172,7 +3169,6 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod, bool src_id_verbatim) cons // Transfer src across designs. Both modules must be attached // to a design for the migration to happen; in the // detached-clone() scratch flow (equiv_make, etc.) src is - // dropped here — those callers don't preserve src across the // temp clone by design. if (this->design && new_mod->design) copy_src_into(this, this->design, new_mod, new_mod->design); @@ -3189,7 +3185,6 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod, bool src_id_verbatim) cons return; // Preserve name already set by addWire/addCell (in dst's pool). TwineRef saved_name = dst_obj->meta_ ? dst_obj->meta_->name : Twine::Null; - // Recycle old meta slot (no field releases — name's retain lives on). if (dst_obj->meta_) { dst_obj->meta_->name = Twine::Null; dst_obj->meta_->src = Twine::Null; @@ -3274,7 +3269,6 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod, bool src_id_verbatim) cons // TwinePool allocates slots sequentially as copy_src_into → // copy_from interns each wire's src, so the destination pool // ends up with leaves in the same order the frontend - // originally interned them — that lets write_rtlil emit // byte-equal "@N" refs across single-module clones into an // existing destination design. // Re-intern each wire/cell name from the source design's pool into @@ -3472,7 +3466,6 @@ void RTLIL::Module::add(RTLIL::Process *process) processes[process->meta_->name] = process; process->module = this; // Propagate module back-pointer to every CaseRule/SwitchRule in the - // root case tree and every MemWriteAction in the sync rules — so the // per-Design src meta vector can be resolved from any inner-process // AttrObject via `module->design` after attach. process->root_case.setModuleRecursive(this); @@ -3853,7 +3846,6 @@ RTLIL::Memory *RTLIL::Module::addMemory(TwineRef name, const RTLIL::Memory *othe mem->size = other->size; mem->attributes = other->attributes; { - // Clone path drops src for now — caller responsible for migrating // src across the design boundary if needed. addMemory(name) is the // common case. (void)other; @@ -6783,7 +6775,6 @@ RTLIL::CaseRule *RTLIL::CaseRule::clone() const new_caserule->compare = compare; new_caserule->actions = actions; new_caserule->attributes = attributes; - // clone() drops src — CaseRule has no pool backpointer, so we can't // retain. The caller (Module::addProcess(name, other)) is responsible // for walking the cloned tree and migrating src via context. for (auto &it : switches) @@ -6807,7 +6798,6 @@ RTLIL::SwitchRule *RTLIL::SwitchRule::clone() const RTLIL::SwitchRule *new_switchrule = new RTLIL::SwitchRule; new_switchrule->signal = signal; new_switchrule->attributes = attributes; - // clone() drops src — see CaseRule::clone for rationale. for (auto &it : cases) new_switchrule->cases.push_back(it->clone()); return new_switchrule; @@ -6821,7 +6811,6 @@ RTLIL::SyncRule *RTLIL::SyncRule::clone() const new_syncrule->signal = signal; new_syncrule->actions = actions; new_syncrule->mem_write_actions = mem_write_actions; - // Drop meta_idx_ on the cloned MemWriteActions — the integer was // copied by the vector assignment above without registering with // any pool; the caller is responsible for migrating src across the // clone via context (see Process::clone). @@ -6974,7 +6963,6 @@ void RTLIL::Memory::absorb_attrs(dict &&buf) module->design->absorb_attrs(this, std::move(buf)); } -// CaseRule / SwitchRule / MemWriteAction src helpers — all delegate to // module->design->obj_* via the back-pointer added in the earlier commit. TwineRef RTLIL::CaseRule::src_id() const { diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 7862da501..42e6150c8 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -555,12 +555,16 @@ struct RTLIL::IdString // often one needs to check if a given IdString is part of a list (for example a list // of cell types). the following functions helps with that. + // Constrained to 2+ args so a single argument always resolves to a concrete + // overload below; otherwise a single argument matching none of them (e.g. a + // TwineRef) would re-match this template and recurse infinitely. template - bool in(const Args &... args) const { + bool in(const Args &... args) const requires (sizeof...(Args) != 1) { return (... || in(args)); } bool in(IdString rhs) const { return *this == rhs; } + inline bool in(TwineRef rhs) const; bool in(const char *rhs) const { return *this == rhs; } bool in(const std::string &rhs) const { return *this == rhs; } inline bool in(const pool &rhs) const; @@ -600,6 +604,8 @@ inline bool operator==(TwineRef a, RTLIL::IdString b) { } inline bool operator==(RTLIL::IdString a, TwineRef b) { return b == a; } +inline bool RTLIL::IdString::in(TwineRef rhs) const { return *this == rhs; } + struct RTLIL::OwningIdString : public RTLIL::IdString { inline OwningIdString() { } inline OwningIdString(const OwningIdString &str) : IdString(str) { get_reference(); } @@ -1402,7 +1408,6 @@ inline bool operator!=(RTLIL::IdString lhs, const RTLIL::NameMasqBase & // Read-only masquerade for Wire::name. Reads materialise the TwineRef in // the owning Design's twines pool into a temporary IdString. Writes are -// intentionally unsupported — use Module::rename(wire, new_name) instead. // Defined before Wire so it can be used as a [[no_unique_address]] member. struct RTLIL::WireNameMasq : RTLIL::NameMasqBase { WireNameMasq() = default; @@ -2194,7 +2199,6 @@ struct RTLIL::Design // Wholesale-copy this design into `dst`. `dst` must be empty (no // modules). Copies twines verbatim and clones each module - // preserving src_id_ values directly — avoids the per-module // copy_from pool rebuild and yields byte-identical RTLIL output // across design -push/-pop, -save/-load, etc. void clone_into(RTLIL::Design *dst) const; @@ -2325,7 +2329,6 @@ private: friend struct RTLIL::Patch; public: // Shadows NamedObject::name. Reads materialise via twines; writes - // are a compile error — use Module::rename(wire, new_name) instead. [[no_unique_address]] RTLIL::WireNameMasq name; Hasher::hash_t hashidx_; @@ -2395,7 +2398,6 @@ struct RTLIL::Memory : public RTLIL::AttrObject Memory(); ~Memory(); - // Back-pointer to the owning module — same role as Cell::module / // Wire::module. Set by Module::addMemory / the frontends that // construct Memory free-standing before attaching to a module. // Lets Memory's src access resolve uniformly via module->design. @@ -2436,7 +2438,6 @@ private: bool bufnorm_handle_setPort(TwineRef portname, RTLIL::SigSpec &signal, dict::iterator conn_it); public: // Shadows NamedObject::name. Reads materialise via twines; writes - // are a compile error — use Module::rename(cell, new_name) instead. [[no_unique_address]] RTLIL::CellNameMasq name; Hasher::hash_t hashidx_; @@ -2525,7 +2526,6 @@ struct RTLIL::CaseRule : public RTLIL::AttrObject // Walk the whole CaseRule subtree (this case, every switch, every // nested case, every MemWriteAction inside this process's sync rules - // — those are reached through Process, not here) and set `module` on // each. Idempotent. void setModuleRecursive(RTLIL::Module *m); @@ -2706,7 +2706,6 @@ inline Hasher RTLIL::SigBit::hash_into(Hasher h) const { inline Hasher RTLIL::SigBit::hash_top() const { Hasher h; if (wire) { - // Use the wire's name (TwineRef) directly — avoids IdString materialisation. TwineRef name = wire->meta_ ? wire->meta_->name : Twine::Null; h.eat(name); h.eat(offset); @@ -3075,7 +3074,6 @@ public: virtual RTLIL::Module *clone() const; // Clone variant that attaches the new module to `dst` BEFORE cloneInto // runs. This is the right pattern when the destination design is known - // up front — it avoids the "detached module, attach later" flow and // the pending-literal src stashing it entails. Subtypes override to // preserve their type (AstModule). `src_id_verbatim` is forwarded to // cloneInto. @@ -3104,7 +3102,6 @@ public: return design->selected_member(meta_->name, member->meta_->name); } - // Primary (fast) overloads — key directly into the dict. RTLIL::Wire* wire(TwineRef id) { auto it = wires_.find(id); return it == wires_.end() ? nullptr : it->second; diff --git a/kernel/rtlil_bufnorm.cc b/kernel/rtlil_bufnorm.cc index 510ddf582..b340ed76f 100644 --- a/kernel/rtlil_bufnorm.cc +++ b/kernel/rtlil_bufnorm.cc @@ -1172,7 +1172,6 @@ bool RTLIL::Cell::bufnorm_handle_setPort(TwineRef portname, SigSpec &signal, dic } auto dir = port_dir(portname); - // Fast path: connecting a full driverless wire to an output port — everything else // goes through the bufnorm queues and is handled during the next bufNormalize call if ((dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) && signal.is_wire()) { Wire *w = signal.as_wire(); diff --git a/kernel/twine.cc b/kernel/twine.cc index 28ccb3a38..b2ad0bcbb 100644 --- a/kernel/twine.cc +++ b/kernel/twine.cc @@ -145,7 +145,6 @@ void twine_prepopulate() { // log_assert(parent->is_flat() && "Suffix parent must be a flat node (Leaf or Suffix)"); // if (tail.empty()) { // // No tail means "the same string as parent". Hand back a fresh -// // owning ref on parent — semantically equivalent to a degenerate // // suffix node, but we avoid allocating a slot for it. // retain(parent); // return parent; diff --git a/kernel/twine.h b/kernel/twine.h index 8fb4cf2c4..ae2f8f1cf 100644 --- a/kernel/twine.h +++ b/kernel/twine.h @@ -109,7 +109,6 @@ struct TW { }; #define TW(id) ((size_t)std::integral_constant::value) -// #define TW(name) TW::lookup(#name) struct Twine { static constexpr TwineRef Null = std::numeric_limits::max(); @@ -120,19 +119,31 @@ struct Twine { auto operator<=>(const Suffix&) const = default; }; - struct AutoPrefix { + + struct AutoSuffix { const std::string *prefix; std::string tail; - auto operator<=>(const AutoPrefix&) const = default; + auto operator<=>(const AutoSuffix&) const = default; }; - std::variant, Suffix, AutoPrefix> data; + std::variant< + // Unused slot + std::monostate, + // "leaf", regular deduplicated string + std::string, + // "concat", for src only, requires concatenating character convention - '|' for src attributes, others for others in the future + std::vector, + // "suffix", deduplicates shared prefixes + Suffix, + // transient suffix constructed with NEW_TWINE and NEW_TWINE_SUFFIX + // turned into a regular Suffix when added to a TwinePool + AutoSuffix> data; bool is_dead() const { return std::holds_alternative(data); } bool is_leaf() const { return std::holds_alternative(data); } bool is_concat() const { return std::holds_alternative>(data); } bool is_suffix() const { return std::holds_alternative(data); } - bool is_auto_prefix() const { return std::holds_alternative(data); } + bool is_auto_prefix() const { return std::holds_alternative(data); } bool is_flat() const { return is_leaf() || is_suffix(); } const std::string &leaf() const { return std::get(data); } const std::vector &children() const { return std::get>(data); } @@ -150,7 +161,6 @@ struct TwineHash { size_t operator()(const Twine& t) const noexcept; size_t operator()(TwineRef ref) const noexcept; - // size_t operator()(std::string_view v) const noexcept; }; struct TwineEq { @@ -161,8 +171,6 @@ struct TwineEq { bool operator()(TwineRef a, TwineRef b) const noexcept; bool operator()(TwineRef a, const Twine& b) const noexcept; bool operator()(const Twine& a, TwineRef b) const noexcept; - // bool operator()(TwineRef a, std::string_view b) const noexcept; - // bool operator()(std::string_view a, TwineRef b) const noexcept; }; @@ -365,12 +373,8 @@ struct TwinePool { return ref; } - // Interns a structural Twine verbatim and returns the handle. Leaf content - // is stored as-is — callers holding an escaped string must strip the '\' - // and tag publicity themselves (or use the add(std::string) overload). - // Suffix names inherit the prefix handle's publicity. TwineRef add(Twine t) { - if (auto *ap = std::get_if(&t.data)) { + if (auto *ap = std::get_if(&t.data)) { TwineRef pref = add_inner(Twine{*ap->prefix}); return add_inner(Twine{Twine::Suffix{pref, std::move(ap->tail)}}); } @@ -612,7 +616,7 @@ struct DeepTwineEq { // Required by unordered_set to handle hash collisions between two TwineRefs. bool operator()(TwineRef a, TwineRef b) const { if (a == b) return true; // Index or structural equality shortcut - std::string fb = flatten(b); + std::string fb = pool->unescaped_str(b); return (*this)(a, std::string_view(fb)); } @@ -660,7 +664,7 @@ struct TwineChildPool { // Local analog of TwinePool::add; see there for the convention. TwineRef add(Twine t) { - if (auto *ap = std::get_if(&t.data)) { + if (auto *ap = std::get_if(&t.data)) { TwineRef pref = add_inner(Twine{*ap->prefix}); return add_inner(Twine{Twine::Suffix{pref, std::move(ap->tail)}}); } @@ -731,6 +735,11 @@ struct TwineSearch { index.insert(STATIC_TWINE_END + idx); } } + // Keep a hoisted search current after adding a ref to the pool, so the + // search need not be rebuilt (O(pool)) between finds in a loop. + void insert(TwineRef ref) { + index.insert(twine_untag(ref)); + } // Escaped-name aware. Resolves both statics and locals by content. TwineRef find(std::string_view sv) const { bool is_public = !sv.empty() && sv[0] == '\\'; diff --git a/kernel/unstable/patch.cc b/kernel/unstable/patch.cc index 4e753de73..79ff5c342 100644 --- a/kernel/unstable/patch.cc +++ b/kernel/unstable/patch.cc @@ -134,7 +134,6 @@ namespace { void apply_src(Module* mod, Cell* root, const std::vector& extras, const std::vector& targets, Cell* merge_src_into) { - // Without a design there's no pool — the cells can't carry typed // src, so silently drop merge-of-src in that path. if (!mod || !mod->design) return; @@ -205,7 +204,6 @@ void Patch::patch(Cell* root_cell, TwineRef old_port, SigSpec new_sig, apply_src(mod, root_cell, extras, committed, merge_src_into); // Drop root_cell's driver on the output port BEFORE wiring old_sig to - // new_sig — otherwise old_sig would briefly have two drivers (root_cell // and new_sig) which signorm flags as conflicting. root_cell->unsetPort(old_port); @@ -213,7 +211,6 @@ void Patch::patch(Cell* root_cell, TwineRef old_port, SigSpec new_sig, map->add(old_sig, new_sig); mod->connect_incremental(old_sig, new_sig); - // Remove root cell only — no input-cone walk. mod->remove(root_cell); } @@ -277,7 +274,6 @@ void Patch::commit_inheriting_src(Cell* src_source) { Cell *committed = commit_cell(std::move(cell)); // commit_cell attaches the cell to mod, so adopt_src_from can // now resolve the pool via committed->module->design. Direct - // id transfer — no flatten/re-intern detour. if (src_source) committed->adopt_src_from(src_source); } diff --git a/kernel/unstable/patch.h b/kernel/unstable/patch.h index 46985f03b..e6392dfa0 100644 --- a/kernel/unstable/patch.h +++ b/kernel/unstable/patch.h @@ -6,7 +6,6 @@ YOSYS_NAMESPACE_BEGIN -// No virtual methods — subclasses cannot be dispatched through a Patch pointer. struct RTLIL::Patch : public CellAdderMixin { protected: diff --git a/kernel/utils.h b/kernel/utils.h index f6db48030..dd0ce6168 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -127,7 +127,6 @@ public: // --------------------------------------------------- -// BitGrouper — partition output bits by a per-bit key // --------------------------------------------------- // // Many passes that split a multi-bit cell or word-level FF into smaller diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index 00da6cf0e..6b5c4c79b 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -305,12 +305,12 @@ RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view #define NEW_ID_SUFFIX(suffix) \ YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix) #define NEW_TWINE \ - YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoPrefix{[](std::string_view func) -> const std::string * { \ + YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> const std::string * { \ static std::unique_ptr prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \ return prefix.get(); \ }(__FUNCTION__), std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}} #define NEW_TWINE_SUFFIX(suffix) \ - YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoPrefix{[](std::string_view func) -> const std::string * { \ + YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> const std::string * { \ static std::unique_ptr prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \ return prefix.get(); \ }(__FUNCTION__), std::string(suffix) + "$" + std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}} diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 3a89fc0cd..a5d719e46 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -5,6 +5,7 @@ endif OBJS += passes/cmds/add.o OBJS += passes/cmds/delete.o OBJS += passes/cmds/design.o +OBJS += passes/cmds/dump_twines.o OBJS += passes/cmds/design_equal.o OBJS += passes/cmds/select.o OBJS += passes/cmds/show.o diff --git a/passes/cmds/printattrs.cc b/passes/cmds/printattrs.cc index f0f0c8b19..f4293f674 100644 --- a/passes/cmds/printattrs.cc +++ b/passes/cmds/printattrs.cc @@ -55,7 +55,6 @@ struct PrintAttrsPass : public Pass { } static void log_src(const RTLIL::Design *design, const RTLIL::AttrObject *obj, const unsigned int indent) { - // Emit src outside the attributes loop — it lives on the typed // meta-vector slot, not in obj->attributes. if (design && design->obj_src_id(obj) != Twine::Null) log("%s(* src=\"%s\" *)\n", get_indent_str(indent), diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 66c034e23..39c6cc17a 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -145,7 +145,6 @@ static bool match_attr(const dict &attributes, co // them after the migration of src out of the attribute dict by synthesizing // a one-element dict view containing the flattened ID::src and feeding it // through the normal match_attr path. Other ID::src-shaped patterns like -// wildcards still flow through the (empty) dict and won't match src — those // uses were rare and the dict-path migration is a separate concern. static bool match_attr(const RTLIL::Design *design, const RTLIL::AttrObject *obj, const std::string &match_expr) { diff --git a/passes/cmds/splitcells.cc b/passes/cmds/splitcells.cc index b42c47061..9dd169d16 100644 --- a/passes/cmds/splitcells.cc +++ b/passes/cmds/splitcells.cc @@ -98,7 +98,7 @@ struct SplitcellsWorker std::string s = cell->name.str() + (slice_msb == slice_lsb ? stringf("%c%d%c", format[0], slice_lsb, format[1]) : stringf("%c%d%c%d%c", format[0], slice_msb, format[2], slice_lsb, format[1])); - TwineRef slice_name = module->uniquify(module->design->twines.add(Twine{s})); + TwineRef slice_name = module->uniquify(module->design->twines.add(std::move(s))); Cell *slice = module->addCell(slice_name, cell); @@ -127,7 +127,7 @@ struct SplitcellsWorker if (slice->hasParam(ID::WIDTH)) slice->setParam(ID::WIDTH, GetSize(slice->getPort(TW::Y))); - log(" slice %d: %s => %s\n", i, slice_name, log_signal(slice->getPort(TW::Y))); + log(" slice %d: %s => %s\n", i, module->design->twines.str(slice_name).c_str(), log_signal(slice->getPort(TW::Y))); } module->remove(cell); @@ -162,11 +162,11 @@ struct SplitcellsWorker int slice_msb = slices[i]-1; int slice_lsb = slices[i-1]; - TwinePool twines = module->design->twines; + TwinePool &twines = module->design->twines; std::string s = cell->name.str() + (slice_msb == slice_lsb ? stringf("%c%d%c", format[0], slice_lsb, format[1]) : stringf("%c%d%c%d%c", format[0], slice_msb, format[2], slice_lsb, format[1])); - TwineRef slice_name = module->uniquify(twines.add(Twine{s})); + TwineRef slice_name = module->uniquify(twines.add(std::move(s))); Cell *slice = module->addCell(slice_name, cell); diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index 6cc2fc386..c977908d3 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -55,7 +55,7 @@ struct SplitnetsWorker if (format.size() > 1) new_wire_name += format.substr(1, 1); - RTLIL::Wire *new_wire = module->addWire(module->uniquify(module->design->twines.add(Twine{new_wire_name})), width); + RTLIL::Wire *new_wire = module->addWire(module->uniquify(module->design->twines.add(std::move(new_wire_name))), width); new_wire->port_id = wire->port_id ? wire->port_id + offset : 0; new_wire->port_input = wire->port_input; new_wire->port_output = wire->port_output; diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc index d5f79491a..1e9dad67f 100644 --- a/passes/equiv/equiv_add.cc +++ b/passes/equiv/equiv_add.cc @@ -56,8 +56,9 @@ struct EquivAddPass : public Pass { if (GetSize(args) == 4 && args[1] == "-cell") { - Cell *gold_cell = module->cell(TwineSearch(&design->twines).find(RTLIL::escape_id(args[2]))); - Cell *gate_cell = module->cell(TwineSearch(&design->twines).find(RTLIL::escape_id(args[3]))); + TwineSearch search(&design->twines); + Cell *gold_cell = module->cell(search.find(RTLIL::escape_id(args[2]))); + Cell *gate_cell = module->cell(search.find(RTLIL::escape_id(args[3]))); if (gold_cell == nullptr) { if (try_mode) { diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index f389a5f6f..8e2ec254d 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -154,10 +154,11 @@ struct EquivMakeWorker // list of cells without added $equiv cells auto cells_list = equiv_mod->cells().to_vector(); + TwineSearch search(&equiv_mod->design->twines); for (auto id : wire_names) { - TwineRef gold_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gold"); - TwineRef gate_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gate"); + TwineRef gold_id = search.find(id.str() + "_gold"); + TwineRef gate_id = search.find(id.str() + "_gate"); Wire *gold_wire = equiv_mod->wire(gold_id); Wire *gate_wire = equiv_mod->wire(gate_id); @@ -331,10 +332,11 @@ struct EquivMakeWorker { SigMap assign_map(equiv_mod); + TwineSearch search(&equiv_mod->design->twines); for (auto id : cell_names) { - TwineRef gold_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gold"); - TwineRef gate_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gate"); + TwineRef gold_id = search.find(id.str() + "_gold"); + TwineRef gate_id = search.find(id.str() + "_gate"); Cell *gold_cell = equiv_mod->cell(gold_id); Cell *gate_cell = equiv_mod->cell(gate_id); @@ -380,7 +382,7 @@ struct EquivMakeWorker } equiv_mod->remove(gate_cell); - equiv_mod->rename(gold_cell, TwineSearch(&equiv_mod->design->twines).find(id.str())); + equiv_mod->rename(gold_cell, search.find(id.str())); } } @@ -492,9 +494,10 @@ struct EquivMakePass : public Pass { if (argidx+3 != args.size()) log_cmd_error("Invalid number of arguments.\n"); - worker.gold_mod = design->module(TwineSearch(&design->twines).find(RTLIL::escape_id(args[argidx]))); - worker.gate_mod = design->module(TwineSearch(&design->twines).find(RTLIL::escape_id(args[argidx+1]))); - worker.equiv_mod = design->module(TwineSearch(&design->twines).find(RTLIL::escape_id(args[argidx+2]))); + TwineSearch search(&design->twines); + worker.gold_mod = design->module(search.find(RTLIL::escape_id(args[argidx]))); + worker.gate_mod = design->module(search.find(RTLIL::escape_id(args[argidx+1]))); + worker.equiv_mod = design->module(search.find(RTLIL::escape_id(args[argidx+2]))); if (worker.gold_mod == nullptr) log_cmd_error("Can't find gold module %s.\n", args[argidx]); diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index 65cc64c07..47637425d 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -156,17 +156,20 @@ struct EquivMiterWorker struct RewriteSigSpecWorker { RTLIL::Module * mod; + TwineSearch *search; void operator()(SigSpec &sig) { vector chunks = sig.chunks(); for (auto &c : chunks) if (c.wire != NULL) - c.wire = mod->wire(TwineSearch(&mod->design->twines).find(c.wire->name.str())); + c.wire = mod->wire(search->find(c.wire->name.str())); sig = chunks; } }; RewriteSigSpecWorker rewriteSigSpecWorker; + TwineSearch rewrite_search(&miter_module->design->twines); rewriteSigSpecWorker.mod = miter_module; + rewriteSigSpecWorker.search = &rewrite_search; miter_module->rewrite_sigspecs(rewriteSigSpecWorker); // find undriven or unused wires diff --git a/passes/hierarchy/flatten.cc b/passes/hierarchy/flatten.cc index f4e65538e..2a9659c14 100644 --- a/passes/hierarchy/flatten.cc +++ b/passes/hierarchy/flatten.cc @@ -48,46 +48,61 @@ struct module_ptr_compare { } }; -IdString concat_name(RTLIL::Cell *cell, IdString const &object_name, const std::string &separator = ".") +// Split an object's escaped name into the shared per-instance prefix and the +// remaining tail; prefix + tail is the flattened escaped name. flatten_cell +// interns the prefix once and shares it via a Suffix node (like techmap's +// apply_prefix_ref) instead of storing a full leaf string per object. +std::pair hier_name_parts(RTLIL::Cell *cell, std::string_view object_name_view, const std::string &separator) { - std::string_view object_name_view(object_name.c_str()); - if (object_name_view[0] == '\\'){ - return concat_views(cell->name.str(), separator, object_name_view.substr(1)); - } + if (!object_name_view.empty() && object_name_view[0] == '\\') + return {cell->name.str() + separator, std::string(object_name_view.substr(1))}; constexpr std::string_view prefix = "$flatten"; - if (object_name_view.substr(0, prefix.size()) == prefix){ + if (object_name_view.substr(0, prefix.size()) == prefix) object_name_view.remove_prefix(prefix.size()); + return {"$flatten" + cell->name.str() + separator, std::string(object_name_view)}; +} + +std::string concat_name(RTLIL::Cell *cell, std::string_view object_name_view, const std::string &separator = ".") +{ + auto [prefix, tail] = hier_name_parts(cell, object_name_view, separator); + return prefix + tail; +} + +// Build the flattened name of a template object, given the instance's already +// interned public/private prefix leaves. A name like "\a.b.c.w" is stored as +// nested Suffix nodes Suffix{Suffix{Suffix{"\a.", "b."}, "c."}, "w"}. When the +// template was itself flattened earlier its names are already such Suffixes, so +// flattening instance "u" re-prefixes only the innermost leaf ("\a." -> "\u.a.") +// and reuses the rest, yielding "\u.a.b.c.w" while keeping "b.", "c.", "w" +// shared -- rather than materialising "a.b.c.w" as one fresh tail per object. +TwineRef remap_flattened_name(RTLIL::Design *design, TwineRef obj_ref, + TwineRef pub_prefix_ref, TwineRef priv_prefix_ref, dict &memo) +{ + if (auto it = memo.find(obj_ref); it != memo.end()) + return it->second; + + const Twine &node = design->twines[obj_ref]; + TwineRef result; + if (node.is_suffix()) { + const Twine::Suffix &sfx = node.suffix(); + TwineRef prefix = remap_flattened_name(design, twine_tag(sfx.prefix, obj_ref.is_public()), + pub_prefix_ref, priv_prefix_ref, memo); + result = design->twines.add(Twine{Twine::Suffix{prefix, sfx.tail}}); + } else { + std::string escaped = design->twines.str(obj_ref); + std::string_view obj = escaped; + if (!obj.empty() && obj[0] == '\\') { + result = design->twines.add(Twine{Twine::Suffix{pub_prefix_ref, std::string(obj.substr(1))}}); + } else { + constexpr std::string_view flatten_prefix = "$flatten"; + if (obj.substr(0, flatten_prefix.size()) == flatten_prefix) + obj.remove_prefix(flatten_prefix.size()); + result = design->twines.add(Twine{Twine::Suffix{priv_prefix_ref, std::string(obj)}}); + } } - return concat_views(prefix, cell->name.str(), separator, object_name_view); -} - -template -TwineRef map_name(RTLIL::Cell *cell, T *object, const std::string &separator = ".") -{ - return cell->module->uniquify(cell->module->design->twines.add(Twine{concat_name(cell, object->name, separator).str()})); -} - -// Specialization for Memory -template<> -TwineRef map_name(RTLIL::Cell *cell, RTLIL::Memory *object, const std::string &separator) -{ - auto design = cell->module->design; - std::string obj_name(design->twines.str(object->meta_->name)); - IdString obj_name_id(obj_name); - std::string mapped_name = concat_name(cell, obj_name_id, separator).str(); - return cell->module->uniquify(design->twines.add(Twine{mapped_name})); -} - -// Specialization for Process -template<> -TwineRef map_name(RTLIL::Cell *cell, RTLIL::Process *object, const std::string &separator) -{ - auto design = cell->module->design; - std::string obj_name(design->twines.str(object->meta_->name)); - IdString obj_name_id(obj_name); - std::string mapped_name = concat_name(cell, obj_name_id, separator).str(); - return cell->module->uniquify(design->twines.add(Twine{mapped_name})); + memo[obj_ref] = result; + return result; } void map_sigspec(const dict &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr) @@ -152,15 +167,22 @@ struct FlattenWorker } } - void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector &new_cells, const std::string &separator) + void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector &new_cells, const std::string &separator, const dict &hier_wires) { // Copy the contents of the flattened cell - dict memory_map; + TwineRef pub_prefix_ref = design->twines.add(cell->name.str() + separator); + TwineRef priv_prefix_ref = design->twines.add("$flatten" + cell->name.str() + separator); + dict remap_memo; + auto make_name = [&](TwineRef obj_ref) -> TwineRef { + return module->uniquify(remap_flattened_name(design, obj_ref, pub_prefix_ref, priv_prefix_ref, remap_memo)); + }; + + dict memory_map; for (auto &tpl_memory_it : tpl->memories) { - RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second, separator), tpl_memory_it.second); + RTLIL::Memory *new_memory = module->addMemory(make_name(tpl_memory_it.second->meta_->name), tpl_memory_it.second); map_attributes(cell, new_memory, design->twines.str(tpl_memory_it.second->meta_->name)); - memory_map[tpl_memory_it.first] = new_memory->meta_->name; + memory_map[design->twines.str(tpl_memory_it.first)] = new_memory->meta_->name; design->select(module, new_memory); } @@ -172,8 +194,9 @@ struct FlattenWorker RTLIL::Wire *new_wire = nullptr; if (tpl_wire->name[0] == '\\') { - std::string wire_name = concat_name(cell, tpl_wire->name, separator).str(); - RTLIL::Wire *hier_wire = module->wire(TwineSearch(&design->twines).find(wire_name)); + std::string wire_name = concat_name(cell, tpl_wire->name.str(), separator); + auto hwit = hier_wires.find(wire_name); + RTLIL::Wire *hier_wire = (hwit != hier_wires.end()) ? hwit->second : nullptr; if (hier_wire != nullptr && hier_wire->get_bool_attribute(ID::hierconn)) { hier_wire->attributes.erase(ID::hierconn); if (GetSize(hier_wire) < GetSize(tpl_wire)) { @@ -185,7 +208,7 @@ struct FlattenWorker } } if (new_wire == nullptr) { - new_wire = module->addWire(map_name(cell, tpl_wire, separator), tpl_wire); + new_wire = module->addWire(make_name(tpl_wire->name.ref()), tpl_wire); new_wire->port_input = new_wire->port_output = false; new_wire->port_id = false; } @@ -196,12 +219,11 @@ struct FlattenWorker } for (auto &tpl_proc_it : tpl->processes) { - RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second, separator), tpl_proc_it.second); + RTLIL::Process *new_proc = module->addProcess(make_name(tpl_proc_it.second->meta_->name), tpl_proc_it.second); map_attributes(cell, new_proc, design->twines.str(tpl_proc_it.second->meta_->name)); for (auto new_proc_sync : new_proc->syncs) for (auto &memwr_action : new_proc_sync->mem_write_actions) { - TwineRef old_memid_ref = TwineSearch(&design->twines).find(memwr_action.memid.str()); - memwr_action.memid = design->twines.str(memory_map.at(old_memid_ref)); + memwr_action.memid = design->twines.str(memory_map.at(memwr_action.memid.str())); } auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; new_proc->rewrite_sigspecs(rewriter); @@ -211,15 +233,14 @@ struct FlattenWorker for (auto tpl_cell : tpl->cells()) { if (tpl_cell->type.in(TW($input_port), TW($output_port), TW($public))) continue; - RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell, separator), tpl_cell); + RTLIL::Cell *new_cell = module->addCell(make_name(tpl_cell->name.ref()), tpl_cell); map_attributes(cell, new_cell, tpl_cell->name); if (new_cell->has_memid()) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); - TwineRef memid_ref = TwineSearch(&design->twines).find(memid.str()); - new_cell->setParam(ID::MEMID, Const(design->twines.str(memory_map.at(memid_ref)))); + new_cell->setParam(ID::MEMID, Const(design->twines.str(memory_map.at(memid.str())))); } else if (new_cell->is_mem_cell()) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); - new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid, separator).str())); + new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid.str(), separator))); } auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; new_cell->rewrite_sigspecs(rewriter); @@ -319,7 +340,6 @@ struct FlattenWorker scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second); } // src lives outside cell->attributes after the typed-src - // migration — fold it into the renamed-attribute view by // hand so `a:cell_src` selectors keep working. if (cell->src_id() != Twine::Null) scopeinfo->attributes.emplace(ID(cell_src), RTLIL::Const(cell->get_src_attribute())); @@ -335,7 +355,7 @@ struct FlattenWorker module->remove(cell); if (scopeinfo != nullptr) - module->rename(scopeinfo, design->twines.add(Twine{cell_name.str()})); + module->rename(scopeinfo, design->twines.add(cell_name.str())); } void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool &used_modules, const std::string &separator) @@ -344,6 +364,15 @@ struct FlattenWorker return; SigMap sigmap(module); + + // hierconn wires are connection points pre-created in `module`; flatten + // reuses them by hierarchical name. Index them once instead of doing a + // pool-wide content search per template wire. + dict hier_wires; + for (auto wire : module->wires()) + if (wire->get_bool_attribute(ID::hierconn)) + hier_wires[wire->name.str()] = wire; + std::vector worklist = module->selected_cells(); while (!worklist.empty()) { @@ -368,7 +397,7 @@ struct FlattenWorker // If a design is fully selected and has a top module defined, topological sorting ensures that all cells // added during flattening are black boxes, and flattening is finished in one pass. However, when flattening // individual modules, this isn't the case, and the newly added cells might have to be flattened further. - flatten_cell(design, module, cell, tpl, sigmap, worklist, separator); + flatten_cell(design, module, cell, tpl, sigmap, worklist, separator, hier_wires); } } }; diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index a383fe0c0..f06e25d52 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -113,7 +113,6 @@ struct MemoryMapWorker std::set static_ports; std::map static_cells_map; - // "@N" ref, not a flattened literal — avoids re-interning a // possibly-Concat src as a single pipe-joined leaf on every // new cell. set_src_attribute's parse_ref path retains the // pool slot directly. diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 2d51312ae..a67536083 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -406,7 +406,6 @@ struct OptMergePass : public Pass { ct.cell_types.erase(TW($anyconst)); ct.cell_types.erase(TW($allseq)); ct.cell_types.erase(TW($allconst)); - // Synthetic driver cells signorm creates for module ports — must // never be folded into one another, otherwise distinct ports collapse. ct.cell_types.erase(TW($input_port)); ct.cell_types.erase(TW($output_port)); diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 1bdec164b..1410f0a1d 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -461,7 +461,6 @@ struct WreduceWorker static int count_nontrivial_wire_attrs(RTLIL::Wire *w) { - // w->attributes no longer holds ID::src — typed src field, not counted. int count = w->attributes.size(); count -= w->attributes.count(ID::unused_bits); return count; diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 09608cf46..dcf716c9a 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -316,7 +316,7 @@ struct SimInstance Module *mod = module->design->module(cell->type_impl); if (mod != nullptr) { - dirty_children.insert(new SimInstance(shared, scope + "." + cell->module->design->twines.str(cell->meta_->name), mod, cell, this)); + dirty_children.insert(new SimInstance(shared, scope + "." + cell->module->design->twines.unescaped_str(cell->meta_->name), mod, cell, this)); } for (auto &port : cell->connections()) { diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index df2f6559d..cd6838538 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -294,14 +294,17 @@ RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit: auto &tw = needle->design->twines; // create new cell - RTLIL::Cell *cell = haystack->addCell(Twine{stringf("$extract$%s$%d", needle->name, autoidx++)}, Twine{needle->name.str()}); + RTLIL::Cell *cell = haystack->addCell(haystack->design->twines.add(stringf("$extract$%s$%d", needle->name, autoidx++)), haystack->design->twines.add(needle->name.str())); - // create cell ports + // create cell ports. Port names come from the needle (map) pool; translate + // them into the haystack pool so the new cell's ports are keyed by the same + // refs as the referenced module in the haystack design. for (auto wire : needle->wires()) { if (wire->port_id > 0) { + TwineRef portname = haystack->design->twines.add(tw.str(wire->meta_->name)); for (int i = 0; i < wire->width; i++) - sig2port.insert(sigmap(RTLIL::SigSpec(wire, i)), std::pair(wire->meta_->name, i)); - cell->setPort(wire->meta_->name, RTLIL::SigSpec(RTLIL::State::Sz, wire->width)); + sig2port.insert(sigmap(RTLIL::SigSpec(wire, i)), std::pair(portname, i)); + cell->setPort(portname, RTLIL::SigSpec(RTLIL::State::Sz, wire->width)); } } @@ -628,7 +631,7 @@ struct ExtractPass : public Pass { if (!mine_mode) for (auto module : map->modules()) { SubCircuit::Graph mod_graph; - std::string graph_name = "needle_" + design->twines.unescaped_str(module->name); + std::string graph_name = "needle_" + map->twines.unescaped_str(module->name); log("Creating needle graph %s.\n", graph_name); if (module2graph(mod_graph, module, constports)) { solver.addGraph(graph_name, mod_graph); @@ -656,8 +659,8 @@ struct ExtractPass : public Pass { for (auto needle : needle_list) for (auto &haystack_it : haystack_map) { - log("Solving for %s in %s.\n", ("needle_" + design->twines.unescaped_str(needle->name)), haystack_it.first); - solver.solve(results, "needle_" + design->twines.unescaped_str(needle->name), haystack_it.first, false); + log("Solving for %s in %s.\n", ("needle_" + map->twines.unescaped_str(needle->name)), haystack_it.first); + solver.solve(results, "needle_" + map->twines.unescaped_str(needle->name), haystack_it.first, false); } log("Found %d matches.\n", GetSize(results)); diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index 20b661abe..0262cc5bd 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -170,7 +170,7 @@ struct MuxcoverWorker return true; } char port_name[3] = {'\\', *path, 0}; - return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(tree.muxes.at(bit)->module->design->twines.add(Twine{std::string(port_name)}))), path+1, false); + return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(tree.muxes.at(bit)->module->design->twines.add(std::string(port_name)))), path+1, false); } else { ret_bit = bit; return true; diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index e924fc6a5..01a4e9f7b 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -535,7 +535,6 @@ struct TechmapWorker extmapper_module = extmapper_design->addModule(extmapper_design->twines.add(std::string{m_name})); RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type.ref(), cell); // addCell(name, cell) already migrated src across - // designs via copy_src_into — no need for an // explicit set_src_attribute round-trip here. int port_counter = 1; diff --git a/techlibs/ice40/ice40_wrapcarry.cc b/techlibs/ice40/ice40_wrapcarry.cc index 06964ca71..0ee095055 100644 --- a/techlibs/ice40/ice40_wrapcarry.cc +++ b/techlibs/ice40/ice40_wrapcarry.cc @@ -62,7 +62,6 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm) cell->attributes[stringf("\\SB_CARRY.%s", a.first)] = a.second; for (const auto &a : st.lut->attributes) cell->attributes[stringf("\\SB_LUT4.%s", a.first)] = a.second; - // src now lives in src_id_, not the attributes dict — propagate it // via prefixed flat-literal attributes so the unwrap pass can restore. if (st.carry->src_id() != Twine::Null) cell->attributes[IdString("\\SB_CARRY.\\src")] = Const(st.carry->get_src_attribute()); diff --git a/tests/memories/generate_mk.py b/tests/memories/generate_mk.py index 93a5bec04..42e186993 100644 --- a/tests/memories/generate_mk.py +++ b/tests/memories/generate_mk.py @@ -7,6 +7,6 @@ import gen_tests_makefile gen_tests_makefile.generate_autotest("*.v", "", """if grep -Eq 'expect-(wr-ports|rd-ports|rd-clk)' $@; then \\ - $(YOSYS) -f verilog -qp "proc; opt; memory -nomap; dump -outfile $(@:.v=).dmp t:\\$$mem_v2" $@; \\ + $(YOSYS) -f verilog -qp "proc; opt; memory -nomap; dump -readable -outfile $(@:.v=).dmp t:\\$$mem_v2" $@; \\ python3 validate.py $@ $(@:.v=).dmp; \\ fi""") diff --git a/tests/rtlil/suffix-chain.il b/tests/rtlil/suffix-chain.il index 975ed9754..8fd88bbcc 100644 --- a/tests/rtlil/suffix-chain.il +++ b/tests/rtlil/suffix-chain.il @@ -1,15 +1,15 @@ autoidx 1 twines - leaf 0 "/home/emil/repo/" - suffix 1 0 "foo/" - suffix 2 1 "bar.v" - suffix 3 2 ":10.1-10.5" - suffix 4 2 ":11.1-11.5" + leaf 1009 "/home/emil/repo/" + suffix 1010 1009 "foo/" + suffix 1011 1010 "bar.v" + suffix 1012 1011 ":10.1-10.5" + suffix 1013 1011 ":11.1-11.5" end module \chain - attribute \src "@3" + attribute \src "@1012" # /home/emil/repo/foo/bar.v:10.1-10.5 wire input 1 \a - attribute \src "@4" + attribute \src "@1013" # /home/emil/repo/foo/bar.v:11.1-11.5 wire output 2 \b connect \b \a end diff --git a/tests/rtlil/suffix-twines.il b/tests/rtlil/suffix-twines.il index adcf58fda..22a95c11a 100644 --- a/tests/rtlil/suffix-twines.il +++ b/tests/rtlil/suffix-twines.il @@ -1,15 +1,16 @@ autoidx 1 twines - leaf 0 "everything.v" - suffix 1 0 ":1.1-1.10" - suffix 2 0 ":2.5-2.8" - concat 3 1 2 + leaf 1009 "everything.v" + suffix 1010 1009 ":1.1-1.10" + suffix 1011 1009 ":2.5-2.8" + concat 1012 1010 1011 + leaf 1013 "tiny" end -attribute \src "@3" -module \tiny - attribute \src "@1" +attribute \src "@1012" # everything.v:1.1-1.10|everything.v:2.5-2.8 +module $pub@1013 # \tiny + attribute \src "@1010" # everything.v:1.1-1.10 wire input 1 \a - attribute \src "@2" + attribute \src "@1011" # everything.v:2.5-2.8 wire output 2 \b connect \b \a end