3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-23 23:52:32 +00:00
This commit is contained in:
Emil J. Tywoniak 2026-06-05 12:04:19 +02:00
parent 3d27e83d0f
commit 3424c00cd0
63 changed files with 2635 additions and 503 deletions

View file

@ -1106,7 +1106,31 @@ std::string AstNode::loc_string() const
void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast)
{
obj->attributes[ID::src] = ast->loc_string();
// 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;
if (!loc.begin.filename || loc.begin.filename->empty()) {
obj->set_src_attribute(&current_module->design->src_twines, ast->loc_string());
return;
}
// Split filename and per-location tail so the filename interns once
// per file and every cell/wire src for that file gets a Suffix node
// carrying only ":line.col-line.col". For a typical large design with
// thousands of objects in one file this collapses N copies of a long
// path into 1 Leaf + N short Suffix tails.
TwinePool *pool = &current_module->design->src_twines;
Twine::Id file_id = pool->intern(*loc.begin.filename);
std::string tail = stringf(":%d.%d-%d.%d",
loc.begin.line, loc.begin.column,
loc.end.line, loc.end.column);
Twine::Id suffix_id = pool->intern_suffix(file_id, tail);
pool->release(file_id); // suffix internally holds a ref now
obj->set_src_id(pool, suffix_id);
pool->release(suffix_id); // set_src_id retained on obj's behalf
}
static bool param_has_no_default(const AstNode* param) {
@ -1130,6 +1154,12 @@ 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->src_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;
module->name = ast->str;
@ -1920,6 +1950,29 @@ RTLIL::Module *AstModule::clone() const
return new_mod;
}
RTLIL::Module *AstModule::clone(RTLIL::Design *dst, bool src_id_verbatim) const
{
AstModule *new_mod = new AstModule;
new_mod->name = name;
dst->add(new_mod);
cloneInto(new_mod, src_id_verbatim);
new_mod->ast = ast->clone();
new_mod->nolatches = nolatches;
new_mod->nomeminit = nomeminit;
new_mod->nomem2reg = nomem2reg;
new_mod->mem2reg = mem2reg;
new_mod->noblackbox = noblackbox;
new_mod->lib = lib;
new_mod->nowb = nowb;
new_mod->noopt = noopt;
new_mod->icells = icells;
new_mod->pwires = pwires;
new_mod->autowire = autowire;
return new_mod;
}
void AstModule::loadconfig() const
{
current_ast = NULL;

View file

@ -402,6 +402,7 @@ namespace AST
void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
bool reprocess_if_necessary(RTLIL::Design *design) override;
RTLIL::Module *clone() const override;
RTLIL::Module *clone(RTLIL::Design *dst, bool src_id_verbatim = false) const override;
void loadconfig() const;
};

View file

@ -167,7 +167,9 @@ static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
const AstNode *node, const char *to_add_kind)
{
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
std::string src = existing->get_string_attribute(ID::src);
std::string src;
if (module->design)
src = existing->get_src_attribute(&module->design->src_twines);
std::string location_str = "earlier";
if (!src.empty())
location_str = "at " + src;

View file

@ -287,6 +287,25 @@ void json_parse_attr_param(dict<IdString, Const> &results, JsonNode *node)
}
}
// AttrObject-aware overload: extracts ID::src and routes it to the typed
// src_id_ field via set_src_attribute. Other keys still land in the
// attributes dict via the generic path.
void json_parse_attributes(TwinePool *pool, RTLIL::AttrObject *obj, JsonNode *node)
{
if (node->type != 'D')
log_error("JSON attributes or parameters node is not a dictionary.\n");
for (auto it : node->data_dict)
{
IdString key = RTLIL::escape_id(it.first.c_str());
Const value = json_parse_attr_param_value(it.second);
if (key == ID::src && (value.flags & RTLIL::CONST_FLAG_STRING))
obj->set_src_attribute(pool, value.decode_string());
else
obj->attributes[key] = value;
}
}
void json_import(Design *design, string &modname, JsonNode *node)
{
log("Importing module %s from JSON tree.\n", modname);
@ -300,7 +319,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
design->add(module);
if (node->data_dict.count("attributes"))
json_parse_attr_param(module->attributes, node->data_dict.at("attributes"));
json_parse_attributes(&design->src_twines, module, node->data_dict.at("attributes"));
if (node->data_dict.count("parameter_default_values"))
json_parse_attr_param(module->parameter_default_values, node->data_dict.at("parameter_default_values"));
@ -483,7 +502,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
}
if (net_node->data_dict.count("attributes"))
json_parse_attr_param(wire->attributes, net_node->data_dict.at("attributes"));
json_parse_attributes(&design->src_twines, wire, net_node->data_dict.at("attributes"));
}
}
@ -564,7 +583,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
}
if (cell_node->data_dict.count("attributes"))
json_parse_attr_param(cell->attributes, cell_node->data_dict.at("attributes"));
json_parse_attributes(&design->src_twines, cell, cell_node->data_dict.at("attributes"));
if (cell_node->data_dict.count("parameters"))
json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters"));
@ -611,7 +630,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
}
if (memory_node->data_dict.count("attributes"))
json_parse_attr_param(mem->attributes, memory_node->data_dict.at("attributes"));
json_parse_attributes(&design->src_twines, mem, memory_node->data_dict.at("attributes"));
module->memories[mem->name] = mem;
}

View file

@ -24,6 +24,7 @@
#include "kernel/register.h"
#include "kernel/log.h"
#include "kernel/utils.h"
#include "kernel/twine.h"
#include <charconv>
#include <deque>
#include <optional>
@ -52,6 +53,14 @@ struct RTLILFrontendWorker {
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->src_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::Id, Twine::Id> twine_remap;
std::vector<Twine::Id> twine_parser_holds;
template <typename... Args>
[[noreturn]]
void error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
@ -436,9 +445,15 @@ struct RTLILFrontendWorker {
current_module = new RTLIL::Module;
current_module->name = std::move(module_name);
current_module->attributes = std::move(attrbuf);
if (!delete_current_module)
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);
current_module->attributes = std::move(attrbuf);
} else {
design->add(current_module);
current_module->absorb_attrs(&design->src_twines, std::move(attrbuf));
}
while (true)
{
@ -491,10 +506,105 @@ 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();
Twine::Id file_id = TwinePool::parse_ref(raw);
if (file_id != Twine::Null) {
// Translate the file-local twine id to the destination
// design's pool id via twine_remap. If the file had no
// `twines` block (legacy) the remap is empty — accept the
// ref verbatim and let downstream code intern it.
auto it = twine_remap.find(file_id);
if (it != twine_remap.end())
c = RTLIL::Const(TwinePool::format_ref(it->second));
} else if (raw.find('|') != std::string::npos) {
log_warning("line %d: src attribute %s contains '|' separators. "
"That convention is Yosys-internal; the producing tool "
"should emit a single path:line.col per attribute and "
"let Yosys merge through the twine pool.\n",
line_num, raw.c_str());
}
}
attrbuf.insert({std::move(id), std::move(c)});
expect_eol();
}
// Parses a `twines` ... `end` block. Builds twine_remap so subsequent
// cell/wire src "@N" references (which use file-local ids) translate
// to ids in design->src_twines. The destination pool may already be
// non-empty (multi-file load) — interned/concated nodes are dedup'd
// against the existing pool by the pool itself. Each parser-side
// retain is tracked in twine_parser_holds and released at end-of-parse
// so only cell/wire references survive.
void parse_twines()
{
expect_eol();
while (true) {
if (try_parse_keyword("end"))
break;
if (try_parse_keyword("leaf")) {
int file_id = static_cast<int>(parse_integer());
std::string text = parse_string();
expect_eol();
Twine::Id local_id = design->src_twines.intern(text);
twine_parser_holds.push_back(local_id);
twine_remap[static_cast<Twine::Id>(file_id)] = local_id;
continue;
}
if (try_parse_keyword("suffix")) {
int file_id = static_cast<int>(parse_integer());
Twine::Id file_parent = static_cast<Twine::Id>(parse_integer());
std::string tail = parse_string();
expect_eol();
auto it = twine_remap.find(file_parent);
if (it == twine_remap.end())
error("twines: suffix %d references undefined parent %u.",
file_id, file_parent);
Twine::Id local_id = design->src_twines.intern_suffix(it->second, tail);
twine_parser_holds.push_back(local_id);
twine_remap[static_cast<Twine::Id>(file_id)] = local_id;
continue;
}
if (try_parse_keyword("concat")) {
int file_id = static_cast<int>(parse_integer());
std::vector<Twine::Id> children;
while (!try_parse_eol()) {
Twine::Id file_child = static_cast<Twine::Id>(parse_integer());
auto it = twine_remap.find(file_child);
if (it == twine_remap.end())
error("twines: concat %d references undefined leaf/concat %u.",
file_id, file_child);
children.push_back(it->second);
}
Twine::Id local_id = design->src_twines.concat(
std::span<const Twine::Id>{children});
twine_parser_holds.push_back(local_id);
twine_remap[static_cast<Twine::Id>(file_id)] = local_id;
continue;
}
error("Expected `leaf`, `suffix` or `concat` inside twines block, got `%s'.",
error_token());
}
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()
{
for (Twine::Id id : twine_parser_holds)
design->src_twines.release(id);
twine_parser_holds.clear();
twine_remap.clear();
}
void parse_parameter()
{
RTLIL::IdString id = parse_id();
@ -556,7 +666,7 @@ struct RTLILFrontendWorker {
error("Unexpected wire option: %s", error_token());
}
wire->attributes = std::move(attrbuf);
wire->absorb_attrs(&design->src_twines, std::move(attrbuf));
wire->width = width;
wire->upto = upto;
wire->start_offset = start_offset;
@ -570,7 +680,7 @@ struct RTLILFrontendWorker {
void parse_memory()
{
RTLIL::Memory *memory = new RTLIL::Memory;
memory->attributes = std::move(attrbuf);
memory->absorb_attrs(&design->src_twines, std::move(attrbuf));
int width = 1;
int start_offset = 0;
@ -638,7 +748,7 @@ struct RTLILFrontendWorker {
error("RTLIL error: redefinition of cell %s.", cell_name);
}
RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type);
cell->attributes = std::move(attrbuf);
cell->absorb_attrs(&design->src_twines, std::move(attrbuf));
while (true)
{
@ -728,7 +838,7 @@ struct RTLILFrontendWorker {
{
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
rule->signal = parse_sigspec();
rule->attributes = std::move(attrbuf);
rule->absorb_attrs(&design->src_twines, std::move(attrbuf));
switch_stack.back()->push_back(rule);
expect_eol();
@ -745,7 +855,7 @@ struct RTLILFrontendWorker {
expect_keyword("case");
RTLIL::CaseRule *case_rule = new RTLIL::CaseRule;
case_rule->attributes = std::move(attrbuf);
case_rule->absorb_attrs(&design->src_twines, std::move(attrbuf));
rule->cases.push_back(case_rule);
switch_stack.push_back(&case_rule->switches);
case_stack.push_back(case_rule);
@ -779,7 +889,7 @@ struct RTLILFrontendWorker {
error("RTLIL error: redefinition of process %s.", proc_name);
}
RTLIL::Process *proc = current_module->addProcess(std::move(proc_name));
proc->attributes = std::move(attrbuf);
proc->absorb_attrs(&design->src_twines, std::move(attrbuf));
switch_stack.clear();
switch_stack.push_back(&proc->root_case.switches);
@ -828,7 +938,7 @@ struct RTLILFrontendWorker {
break;
RTLIL::MemWriteAction act;
act.attributes = std::move(attrbuf);
act.absorb_attrs(&design->src_twines, std::move(attrbuf));
act.memid = parse_id();
act.address = parse_sigspec();
act.data = parse_sigspec();
@ -869,10 +979,15 @@ struct RTLILFrontendWorker {
expect_eol();
continue;
}
if (try_parse_keyword("twines")) {
parse_twines();
continue;
}
error("Unexpected token: %s", error_token());
}
if (attrbuf.size() != 0)
error("dangling attribute");
release_twine_parser_holds();
}
};