3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-23 15:42:32 +00:00

twine: fix replayability, reduce TwineSearch usage

This commit is contained in:
Emil J. Tywoniak 2026-06-22 17:53:19 +02:00
parent e9eb3889b7
commit 7c73fd62e4
41 changed files with 273 additions and 272 deletions

View file

@ -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;

View file

@ -46,6 +46,7 @@ struct AigerReader
std::vector<RTLIL::Wire*> bad_properties;
std::vector<RTLIL::Cell*> boxes;
std::vector<int> mergeability, initial_state;
dict<unsigned, RTLIL::Wire*> 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);
};

View file

@ -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());

View file

@ -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;

View file

@ -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<int, SigBit> signal_bits;
dict<IdString, Wire*> 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')

View file

@ -50,26 +50,14 @@ struct RTLILFrontendWorker {
RTLIL::Module *current_module;
dict<RTLIL::IdString, RTLIL::Const> 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<std::vector<RTLIL::SwitchRule*>*> switch_stack;
std::vector<RTLIL::CaseRule*> 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<size_t, TwineRef> twine_remap;
std::vector<TwineRef> 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<size_t> 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();
}
};