twine: split Twine into in-memory TwineNode that doesn't support autoidx variants and temporary TwineSpec that does

This commit is contained in:
Emil J. Tywoniak
2026-10-02 18:42:01 +02:00
parent 926264784d
commit 6f0e648543
32 changed files with 756 additions and 299 deletions
+2
View File
@@ -45,6 +45,7 @@ option(YOSYS_ENABLE_UNIT_TESTS "Enable unit tests" ON)
option(YOSYS_ENABLE_COVERAGE "Enable code coverage" OFF)
option(YOSYS_ENABLE_PROFILING "Enable instruction profiling" OFF)
option(YOSYS_ENABLE_FUNCTIONAL_TESTS "Enable running functional tests" OFF)
option(YOSYS_ENABLE_TWINE_PROVENANCE "Twine provenance for debugging. Worsens performance" OFF)
set(YOSYS_PROGRAM_PREFIX "" CACHE STRING "Name prefix for programs, libraries, and data")
set(YOSYS_COMPONENTS "everything" CACHE STRING "List of components to build (use pass names)")
@@ -323,6 +324,7 @@ condition(YOSYS_ENABLE_SLANG NOT YOSYS_WITHOUT_SLANG)
add_feature_info(have_glob YOSYS_ENABLE_GLOB "Glob expansion in filenames")
add_feature_info(have_spawn YOSYS_ENABLE_SPAWN "Passes that invoke external tools")
add_feature_info(have_threads YOSYS_ENABLE_THREADS "Multithreaded netlist operations")
add_feature_info(twine_provenance YOSYS_ENABLE_TWINE_PROVENANCE "Twine provenance for debugging. Worsens performance")
add_feature_info(have_plugins YOSYS_ENABLE_PLUGINS "Dynamically loadable binary plugins")
add_feature_info(with_abc YOSYS_ENABLE_ABC "Production-quality logic synthesis flow")
add_feature_info(with_zlib YOSYS_ENABLE_ZLIB "Transparent Gzip decompression and FST file format support")
+11 -6
View File
@@ -142,21 +142,26 @@ void RTLIL_BACKEND::dump_twines(std::ostream &f, const RTLIL::Design *design, co
for (IdString id = used_id; id != IdString::Null && closed.insert(id).second; ) {
ids.push_back(id);
const TwineNode &n = design->twines[id];
id = n.is_suffix() ? n.suffix().prefix.untag() : IdString();
id = n.is_suffix() ? n.prefix() : IdString();
}
std::sort(ids.begin(), ids.end());
f << stringf("twines\n");
for (IdString id : ids) {
const TwineNode &n = design->twines[id];
if (n.is_leaf()) {
switch (n.kind()) {
case TwineNode::Kind::Leaf:
f << stringf(" leaf %zu ", id.raw());
dump_const(f, RTLIL::Const(n.leaf()));
dump_const(f, RTLIL::Const(std::string(n.text())));
f << stringf("\n");
} else if (n.is_suffix()) {
f << stringf(" suffix %zu %zu ", id.raw(), n.suffix().prefix.raw());
dump_const(f, RTLIL::Const(n.suffix().tail));
break;
case TwineNode::Kind::Suffix:
f << stringf(" suffix %zu %zu ", id.raw(), n.prefix().raw());
dump_const(f, RTLIL::Const(std::string(n.text())));
f << stringf("\n");
break;
case TwineNode::Kind::Dead:
break;
}
}
f << stringf("end\n");
+1 -1
View File
@@ -2514,7 +2514,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
}
f << stringf(");\n");
if (!systemverilog && !module->processes.empty()) {
initial_id = Twine(NEW_ID).content_str();
initial_id = TwineSpec(NEW_ID).content_str();
f << indent + " " << "reg " << id(initial_id) << " = 0;\n";
}
+14 -1
View File
@@ -1117,7 +1117,7 @@ static IdString build_hier_content(TwinePool &pool, std::string_view content)
if (dot == std::string_view::npos)
return pool.add(std::string{content}).tag(true);
IdString prefix = build_hier_content(pool, content.substr(0, dot));
return pool.add(Twine::Suffix{prefix, std::string{content.substr(dot)}});
return pool.add(TwineSpec::Suffix{prefix, std::string{content.substr(dot)}});
}
IdString AST::intern_hier_name(RTLIL::Design *design, std::string_view escaped)
@@ -1127,6 +1127,19 @@ IdString AST::intern_hier_name(RTLIL::Design *design, std::string_view escaped)
return design->twines.add(std::string{escaped});
}
IdString AST::intern_src_loc(RTLIL::Design *design, const AstSrcLocType &location)
{
return design->twines.add(stringf("$%s:%d",
RTLIL::encode_filename(*location.begin.filename), location.begin.line));
}
IdString AST::intern_src_name(RTLIL::Design *design, const AstSrcLocType &location,
std::string_view kind, int idx)
{
return design->twines.add(TwineSpec::Suffix{intern_src_loc(design, location),
stringf("%s$%d", kind, idx)});
}
void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast)
{
obj->attributes[ID::src] = ast->loc_string();
+4
View File
@@ -437,6 +437,10 @@ namespace AST
IdString intern_hier_name(RTLIL::Design *design, std::string_view escaped);
IdString intern_src_loc(RTLIL::Design *design, const AstSrcLocType &location);
IdString intern_src_name(RTLIL::Design *design, const AstSrcLocType &location,
std::string_view kind, int idx);
// Helper for setting the src attribute.
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
+32 -28
View File
@@ -44,11 +44,12 @@ using namespace AST_INTERNAL;
// helper function for creating RTLIL code for unary operations
static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true)
{
IdString name = current_module->design->twines.add(stringf("%s$%s:%d$%d", current_module->design->twines.str(type).c_str(), RTLIL::encode_filename(*that->location.begin.filename), that->location.begin.line, autoidx++));
IdString name = intern_src_name(current_module->design, that->location,
current_module->design->twines.str(type), autoidx++);
RTLIL::Cell *cell = current_module->addCell(name, type);
set_src_attr(cell, that);
RTLIL::Wire *wire = current_module->addWire(Twine::Suffix{cell->name, "_Y"}, result_width);
RTLIL::Wire *wire = current_module->addWire(TwineSpec::Suffix{cell->name, "_Y"}, result_width);
set_src_attr(wire, that);
wire->is_signed = that->is_signed;
@@ -76,7 +77,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s
return;
}
IdString name = current_module->design->twines.add(stringf("$extend$%s:%d$%d", RTLIL::encode_filename(*that->location.begin.filename), that->location.begin.line, autoidx++));
IdString name = intern_src_name(current_module->design, that->location, "$extend", autoidx++);
RTLIL::Cell *cell = current_module->addCell(name, ID($pos));
set_src_attr(cell, that);
@@ -103,11 +104,12 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s
// helper function for creating RTLIL code for binary operations
static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
{
IdString name = current_module->design->twines.add(stringf("%s$%s:%d$%d", current_module->design->twines.str(type).c_str(), RTLIL::encode_filename(*that->location.begin.filename), that->location.begin.line, autoidx++));
IdString name = intern_src_name(current_module->design, that->location,
current_module->design->twines.str(type), autoidx++);
RTLIL::Cell *cell = current_module->addCell(name, type);
set_src_attr(cell, that);
RTLIL::Wire *wire = current_module->addWire(Twine::Suffix{cell->name, "_Y"}, result_width);
RTLIL::Wire *wire = current_module->addWire(TwineSpec::Suffix{cell->name, "_Y"}, result_width);
set_src_attr(wire, that);
wire->is_signed = that->is_signed;
@@ -136,10 +138,9 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
{
log_assert(cond.size() == 1);
std::stringstream sstr;
sstr << "$ternary$" << RTLIL::encode_filename(*that->location.begin.filename) << ":" << that->location.begin.line << "$" << (autoidx++);
IdString name = intern_src_name(current_module->design, that->location, "$ternary", autoidx++);
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux));
RTLIL::Cell *cell = current_module->addCell(name, ID($mux));
set_src_attr(cell, that);
RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size());
@@ -351,7 +352,7 @@ struct AST_INTERNAL::ProcessGenerator
LookaheadRewriter la_rewriter(always.get());
// generate process and simple root case
proc = current_module->addProcess(stringf("$proc$%s:%d$%d", RTLIL::encode_filename(*always->location.begin.filename), always->location.begin.line, autoidx++));
proc = current_module->addProcess(intern_src_name(current_module->design, always->location, "$proc", autoidx++));
set_src_attr(proc, always.get());
for (auto &attr : always->attributes) {
if (attr.second->type != AST_CONSTANT)
@@ -797,10 +798,9 @@ struct AST_INTERNAL::ProcessGenerator
case AST_TCALL:
if (ast->str == "$display" || ast->str == "$displayb" || ast->str == "$displayh" || ast->str == "$displayo" ||
ast->str == "$write" || ast->str == "$writeb" || ast->str == "$writeh" || ast->str == "$writeo") {
std::stringstream sstr;
sstr << ast->str << "$" << ast->location.begin.filename << ":" << ast->location.begin.line << "$" << (autoidx++);
IdString name = intern_src_name(current_module->design, ast->location, ast->str, autoidx++);
Wire *en = current_module->addWire(sstr.str() + "_EN", 1);
Wire *en = current_module->addWire(TwineSpec::Suffix{name, "_EN"}, 1);
set_src_attr(en, ast);
proc->root_case.actions.push_back({en, SigSpec(false)});
current_case->actions.push_back({en, SigSpec(true)});
@@ -818,7 +818,7 @@ struct AST_INTERNAL::ProcessGenerator
}
RTLIL::Const polarity = polarity_builder.build();
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
RTLIL::Cell *cell = current_module->addCell(name, ID($print));
set_src_attr(cell, ast);
cell->setParam(ID::TRG_WIDTH, triggers.size());
cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty());
@@ -886,18 +886,19 @@ struct AST_INTERNAL::ProcessGenerator
if (ast->type == AST_FAIR) { flavor = "fair"; desc = "assume (eventually)"; }
if (ast->type == AST_COVER) { flavor = "cover"; desc = "cover ()"; }
std::string cellname;
IdString cellname;
if (ast->str.empty())
cellname = stringf("$%s$%s:%d$%d", flavor, RTLIL::encode_filename(*ast->location.begin.filename), ast->location.begin.line, autoidx++);
cellname = intern_src_name(current_module->design, ast->location,
stringf("$%s", flavor), autoidx++);
else
cellname = ast->str;
check_unique_id(current_module, cellname, ast, "procedural assertion");
cellname = current_module->design->twines.add(std::string{ast->str});
check_unique_id(current_module, current_module->design->twines.str(cellname), ast, "procedural assertion");
RTLIL::SigSpec check = ast->children[0]->genWidthRTLIL(-1, false, &subst_rvalue_map.stdmap());
if (GetSize(check) != 1)
check = current_module->ReduceBool(NEW_ID, check);
Wire *en = current_module->addWire(cellname + "_EN", 1);
Wire *en = current_module->addWire(TwineSpec::Suffix{cellname, "_EN"}, 1);
set_src_attr(en, ast);
proc->root_case.actions.push_back({en, SigSpec(false)});
current_case->actions.push_back({en, SigSpec(true)});
@@ -1957,10 +1958,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// generate $memrd cells for memory read ports
case AST_MEMRD:
{
std::stringstream sstr;
sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++);
IdString name = current_module->design->twines.add(TwineSpec::Suffix{
intern_src_name(current_module->design, location, "$memrd", autoidx++),
stringf("$%s", str)});
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd));
RTLIL::Cell *cell = current_module->addCell(name, ID($memrd));
set_src_attr(cell, this);
IdString mem_tw = current_module->design->twines.find(str);
@@ -1996,12 +1998,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// generate $meminit cells
case AST_MEMINIT:
{
std::stringstream sstr;
sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++);
IdString name = current_module->design->twines.add(TwineSpec::Suffix{
intern_src_name(current_module->design, location, "$meminit", autoidx++),
stringf("$%s", str)});
SigSpec en_sig = children[2]->genRTLIL();
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit_v2));
RTLIL::Cell *cell = current_module->addCell(name, ID($meminit_v2));
set_src_attr(cell, this);
int mem_width, mem_size, addr_bits;
@@ -2041,12 +2044,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (type == AST_FAIR) { flavor = "fair"; desc = "assume property (eventually)"; }
if (type == AST_COVER) { flavor = "cover"; desc = "cover property ()"; }
std::string cellname;
IdString cellname;
if (str.empty())
cellname = stringf("$%s$%s:%d$%d", flavor, RTLIL::encode_filename(*location.begin.filename), location.begin.line, autoidx++);
cellname = intern_src_name(current_module->design, location,
stringf("$%s", flavor), autoidx++);
else
cellname = str;
check_unique_id(current_module, cellname, this, "procedural assertion");
cellname = current_module->design->twines.add(std::string{str});
check_unique_id(current_module, current_module->design->twines.str(cellname), this, "procedural assertion");
RTLIL::SigSpec check = children[0]->genRTLIL();
if (GetSize(check) != 1)
+4 -4
View File
@@ -530,9 +530,9 @@ struct RTLILFrontendWorker {
const TwineDesc &desc = twine_descs.at(id);
IdString found;
if (desc.kind == TwineDesc::Leaf)
found = design->twines.find(Twine{Twine::Leaf{desc.text}});
found = design->twines.find(TwineSpec{TwineSpec::Leaf{desc.text}});
else
found = design->twines.find(Twine{Twine::Suffix{
found = design->twines.find(TwineSpec{TwineSpec::Suffix{
IdString(desc.parent), desc.text}});
if (found == IdString::Null || found.untag().raw() != id)
return false;
@@ -563,10 +563,10 @@ struct RTLILFrontendWorker {
IdString ref;
switch (desc.kind) {
case TwineDesc::Leaf:
ref = design->twines.add(Twine::Leaf{desc.text});
ref = design->twines.add(TwineSpec::Leaf{desc.text});
break;
case TwineDesc::Suffix:
ref = design->twines.add(Twine::Suffix{
ref = design->twines.add(TwineSpec::Suffix{
materialize_file_twine(desc.parent),
desc.text});
break;
+1 -1
View File
@@ -182,7 +182,7 @@ struct FfData : FfTypeData {
pol_set = false;
}
FfData(Module *module, FfInitVals *initvals, Twine &&name)
FfData(Module *module, FfInitVals *initvals, TwineSpec &&name)
: FfData(module, initvals, module->design->twines.add(std::move(name))) {}
FfData(FfInitVals *initvals, Cell *cell_);
+14 -5
View File
@@ -8,8 +8,8 @@
#include <unordered_set>
#include <vector>
/**
* Implements TwinePool shallow deduplicating backing storage
/**
* Implements shallow deduplicating backing storage
*/
YOSYS_NAMESPACE_BEGIN
@@ -21,6 +21,7 @@ struct HashConsNodeHash {
const Derived* pool = nullptr;
size_t operator()(const Node& n) const noexcept { return Derived::hash_node(n); }
size_t operator()(const typename Node::Key& k) const noexcept { return Derived::hash_key(k); }
size_t operator()(Ref ref) const noexcept { return Derived::hash_node((*pool)[ref]); }
};
@@ -44,9 +45,11 @@ struct HashConsPool {
struct NodeEq {
using is_transparent = void;
const Derived* pool = nullptr;
bool operator()(Ref a, Ref b) const noexcept { return (*pool)[a].data == (*pool)[b].data; }
bool operator()(Ref a, const Node& b) const noexcept { return (*pool)[a].data == b.data; }
bool operator()(const Node& a, Ref b) const noexcept { return a.data == (*pool)[b].data; }
bool operator()(Ref a, Ref b) const noexcept { return (*pool)[a] == (*pool)[b]; }
bool operator()(Ref a, const Node& b) const noexcept { return (*pool)[a] == b; }
bool operator()(const Node& a, Ref b) const noexcept { return a == (*pool)[b]; }
bool operator()(Ref a, const typename Node::Key& b) const noexcept { return (*pool)[a] == b; }
bool operator()(const typename Node::Key& a, Ref b) const noexcept { return (*pool)[b] == a; }
};
using Index = std::unordered_set<Ref, NodeHash, NodeEq>;
@@ -184,6 +187,12 @@ public:
return Ref();
}
Ref find_key(const typename Node::Key& k) const {
if (auto it = index.find(k); it != index.end())
return *it;
return Ref();
}
Ref add_inner(Node t) {
Derived::canonicalize(t);
+1 -1
View File
@@ -174,7 +174,7 @@ struct hash_ops {
return hash_ops<u_type>::hash_into((u_type) a, h);
} else if constexpr (std::is_pointer_v<T>) {
return hash_ops<uintptr_t>::hash_into((uintptr_t) a, h);
} else if constexpr (std::is_same_v<T, std::string>) {
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
int size = a.size();
int i = 0;
while (i + 8 < size) {
+1 -1
View File
@@ -223,7 +223,7 @@ struct Mem : RTLIL::AttrObject {
void emulate_read_first(FfInitVals *initvals);
Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(module->design, memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
Mem(Module *module, Twine &&memid, int width, int start_offset, int size)
Mem(Module *module, TwineSpec &&memid, int width, int start_offset, int size)
: Mem(module, module->design->twines.add(std::move(memid)), width, start_offset, size) {}
};
+1 -1
View File
@@ -2955,7 +2955,7 @@ IdString RTLIL::Module::uniquify(IdString name, int &index)
}
while (1) {
IdString new_name = (design->twines.add(Twine::Suffix{name, stringf("_%d", index)})).tag(name.isPublic());
IdString new_name = (design->twines.add(TwineSpec::Suffix{name, stringf("_%d", index)})).tag(name.isPublic());
if (count_id(new_name) == 0)
return new_name;
index++;
+13 -9
View File
@@ -1270,18 +1270,22 @@ struct define_map_t;
template<typename N>
inline constexpr bool is_unpooled_name_v =
std::is_same_v<std::decay_t<N>, Twine> || std::is_same_v<std::decay_t<N>, Twine::Leaf> ||
std::is_same_v<std::decay_t<N>, Twine::Suffix> || std::is_same_v<std::decay_t<N>, Twine::AutoSuffix> ||
std::is_same_v<std::decay_t<N>, TwineSpec> || std::is_same_v<std::decay_t<N>, TwineSpec::Leaf> ||
std::is_same_v<std::decay_t<N>, TwineSpec::Suffix> || std::is_same_v<std::decay_t<N>, TwineSpec::AutoSuffix> ||
std::is_same_v<std::decay_t<N>, std::string> ||
std::is_same_v<std::decay_t<N>, const char*> || std::is_same_v<std::decay_t<N>, char*>;
#define YS_UNPOOLED_NAME(N) std::enable_if_t<is_unpooled_name_v<N>, int> = 0
// Forwarders from various string-representing types into a canonical IdString method
// Forwards _func(name, arg1...)
#define YS_NAME_FWD_POOL(_func, _pool) \
template<typename N, typename... Rest, YS_UNPOOLED_NAME(N)> \
decltype(auto) _func(N name, Rest&&... rest) \
{ return _func(_pool.add(std::move(name)), std::forward<Rest>(rest)...); }
// Forwards _func(arg0, name, arg2...)
#define YS_NAME_FWD_2ND_POOL(_func, _pool) \
template<typename T, typename N, YS_UNPOOLED_NAME(N)> \
decltype(auto) _func(T &&first, N name) \
@@ -1475,7 +1479,7 @@ struct RTLIL::Module : public RTLIL::NamedObject
friend struct RTLIL::Cell;
friend struct RTLIL::Design;
[[no_unique_address]] RTLIL::ModuleNameMasq name;
YS_NO_UNIQUE_ADDRESS RTLIL::ModuleNameMasq name;
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
@@ -1897,7 +1901,7 @@ public:
struct RTLIL::Wire : public RTLIL::NamedObject
{
[[no_unique_address]] RTLIL::WireNameMasq name;
YS_NO_UNIQUE_ADDRESS RTLIL::WireNameMasq name;
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
@@ -1965,7 +1969,7 @@ struct RTLIL::Memory : public RTLIL::NamedObject
RTLIL::Design *design() const { return module ? module->design : nullptr; }
[[no_unique_address]] RTLIL::MemoryNameMasq name;
YS_NO_UNIQUE_ADDRESS RTLIL::MemoryNameMasq name;
int width, start_offset, size;
#ifdef YOSYS_ENABLE_PYTHON
@@ -1989,7 +1993,7 @@ private:
bool bufnorm_handle_setPort(IdString portname, RTLIL::SigSpec &signal, dict<IdString, RTLIL::SigSpec>::iterator conn_it);
public:
[[no_unique_address]] RTLIL::CellNameMasq name;
YS_NO_UNIQUE_ADDRESS RTLIL::CellNameMasq name;
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
@@ -2010,7 +2014,7 @@ public:
RTLIL::Design *design() const { return module ? module->design : nullptr; }
IdString type_impl;
[[no_unique_address]] RTLIL::CellTypeMasq type;
YS_NO_UNIQUE_ADDRESS RTLIL::CellTypeMasq type;
dict<RTLIL::IdString, RTLIL::SigSpec> connections_;
dict<RTLIL::IdString, RTLIL::Const> parameters;
@@ -2130,7 +2134,7 @@ public:
RTLIL::Design *design() const { return module ? module->design : nullptr; }
[[no_unique_address]] RTLIL::ProcessNameMasq name;
YS_NO_UNIQUE_ADDRESS RTLIL::ProcessNameMasq name;
template<typename T> void rewrite_sigspecs(T &functor);
template<typename T> void rewrite_sigspecs2(T &functor);
@@ -2178,7 +2182,7 @@ inline Hasher RTLIL::SigBit::hash_top() const {
Hasher h;
if (wire) {
IdString name = wire->name.ref();
uint32_t n = (uint32_t)name.raw() ^ (uint32_t)(name.raw() >> 32);
uint32_t n = (uint32_t)name.bits();
h.force(hashlib::legacy::djb2_add(n, offset));
return h;
}
+19
View File
@@ -161,4 +161,23 @@ private:
}
namespace hashlib {
template<typename T>
struct masq_hash_ops {
static inline bool cmp(const T &a, const T &b) { return a == b; }
[[nodiscard]] static inline Hasher hash(const T &a) {
return hash_ops<IdString>::hash(a.ref());
}
[[nodiscard]] static inline Hasher hash_into(const T &a, Hasher h) {
return hash_ops<IdString>::hash_into(a.ref(), h);
}
};
template<typename Owner>
struct hash_ops<RTLIL::ObjNameMasq<Owner>> : masq_hash_ops<RTLIL::ObjNameMasq<Owner>> {};
template<> struct hash_ops<RTLIL::CellTypeMasq> : masq_hash_ops<RTLIL::CellTypeMasq> {};
template<> struct hash_ops<RTLIL::ModuleNameMasq> : masq_hash_ops<RTLIL::ModuleNameMasq> {};
template<> struct hash_ops<RTLIL::PooledName> : masq_hash_ops<RTLIL::PooledName> {};
}
#endif
+255 -137
View File
@@ -10,9 +10,8 @@ void StaticTwines::init() {
return;
log_assert(nodes_.empty());
nodes_.reserve(count);
#define X(_id) nodes_.push_back(Twine::Leaf{#_id});
#include "kernel/constids.inc"
#undef X
for (const char *name : ID::static_names)
nodes_.emplace_back(std::string_view(name));
}
const TwineNode &StaticTwines::node(size_t idx) { return nodes_[idx]; }
@@ -21,7 +20,7 @@ bool StaticTwines::ready() { return nodes_.size() == count; }
int64_t twine_gc_ns;
int twine_gc_count;
Hasher IdString::hash_into(Hasher h) const { h.hash64(raw()); return h; }
Hasher IdString::hash_into(Hasher h) const { h.hash64(eq_key()); return h; }
std::string IdString::handle_token() const {
return stringf("%s@%zu", isPublic() ? "$pub" : "$priv", untag().raw());
@@ -53,27 +52,85 @@ std::string ID::unescaped_str(IdString ref) {
return static_names[idx.raw()];
}
Twine::Twine(Leaf v) : data(std::move(v)) {}
Twine::Twine(Suffix v) : data(std::move(v)) {}
Twine::Twine(AutoSuffix v) : data(std::move(v)) {}
TwineSpec::TwineSpec(Leaf v) : data(std::move(v)) {}
TwineSpec::TwineSpec(Suffix v) : data(std::move(v)) {}
TwineSpec::TwineSpec(AutoSuffix v) : data(std::move(v)) {}
bool Twine::is_leaf() const { return std::holds_alternative<Leaf>(data); }
bool Twine::is_suffix() const { return std::holds_alternative<Suffix>(data); }
bool TwineSpec::holds_leaf() const { return std::holds_alternative<Leaf>(data); }
bool TwineSpec::holds_suffix() const { return std::holds_alternative<Suffix>(data); }
TwineNode::TwineNode(Twine::Leaf v) : data(std::move(v)) {}
TwineNode::TwineNode(Twine::Suffix v) : data(std::move(v)) {}
void SmallString::store(std::string_view content)
{
log_assert(content.size() <= MAX_LEN);
release();
len_ = content.size();
if (len_ > INLINE_CAP) {
ptr_ = new char[len_];
memcpy(ptr_, content.data(), len_);
} else {
memcpy(inl_, content.data(), len_);
}
}
bool TwineNode::is_dead() const { return std::holds_alternative<std::monostate>(data); }
bool TwineNode::is_leaf() const { return std::holds_alternative<Twine::Leaf>(data); }
bool TwineNode::is_suffix() const { return std::holds_alternative<Twine::Suffix>(data); }
const std::string &TwineNode::leaf() const { return std::get<Twine::Leaf>(data).s; }
const Twine::Suffix &TwineNode::suffix() const { return std::get<Twine::Suffix>(data); }
void SmallString::release()
{
if (len_ > INLINE_CAP)
delete[] ptr_;
len_ = 0;
}
std::string Twine::content_str() const {
SmallString::SmallString(std::string_view content) { store(content); }
SmallString::SmallString(const SmallString &other) { store(other.view()); }
SmallString::SmallString(SmallString &&other) noexcept : len_(other.len_)
{
memcpy(inl_, other.inl_, INLINE_CAP);
other.len_ = 0;
}
SmallString &SmallString::operator=(const SmallString &other)
{
if (this != &other)
store(other.view());
return *this;
}
SmallString &SmallString::operator=(SmallString &&other) noexcept
{
if (this == &other)
return *this;
release();
memcpy(inl_, other.inl_, INLINE_CAP);
len_ = other.len_;
other.len_ = 0;
return *this;
}
SmallString::~SmallString() { release(); }
TwineNode::TwineNode(TwineNode &&other) noexcept
: text_(std::move(other.text_)), prefix_(other.prefix_)
{
other.prefix_ = DEAD;
}
TwineNode &TwineNode::operator=(TwineNode &&other) noexcept
{
if (this == &other)
return *this;
text_ = std::move(other.text_);
prefix_ = other.prefix_;
other.prefix_ = DEAD;
return *this;
}
std::string TwineSpec::content_str() const {
if (auto *leaf = std::get_if<Leaf>(&data))
return leaf->s;
log_assert(!holds_suffix());
auto &autosfx = std::get<AutoSuffix>(data);
return std::string(autosfx.prefix) + autosfx.tail;
return *autosfx.prefix + autosfx.tail;
}
std::pair<std::string, bool> twine_unescape(std::string s) {
@@ -84,16 +141,26 @@ std::pair<std::string, bool> twine_unescape(std::string s) {
}
TwinePool::TwinePool() : serial_(next_serial()) {}
TwinePool::TwinePool(const TwinePool& other) : HashConsPool(other), serial_(next_serial()) {}
TwinePool::TwinePool(TwinePool&& other) : HashConsPool(std::move(other)), serial_(next_serial()) {}
TwinePool::TwinePool(const TwinePool& other)
: HashConsPool(other), auto_prefixes(other.auto_prefixes), serial_(next_serial()) {}
TwinePool::TwinePool(TwinePool&& other)
: HashConsPool(std::move(other)), auto_prefixes(std::move(other.auto_prefixes)), serial_(next_serial()) {}
TwinePool& TwinePool::operator=(const TwinePool& other) {
if (this == &other)
return *this;
HashConsPool::operator=(other);
auto_prefixes = other.auto_prefixes;
serial_ = next_serial();
return *this;
}
TwinePool& TwinePool::operator=(TwinePool&& other) {
if (this == &other)
return *this;
HashConsPool::operator=(std::move(other));
auto_prefixes = std::move(other.auto_prefixes);
serial_ = next_serial();
return *this;
}
@@ -110,9 +177,8 @@ IdString TwinePool::stamp(IdString ref) const {
}
void TwinePool::check_owned(IdString ref) const {
#ifndef NDEBUG
log_assert(owns(ref));
#endif
if constexpr (IdString::MAX_SERIAL != 0)
log_assert(owns(ref));
}
const TwineNode& TwinePool::operator[](IdString ref) const {
@@ -123,15 +189,15 @@ const TwineNode& TwinePool::operator[](IdString ref) const {
const TwineNode& TwinePool::static_node(size_t idx) { return StaticTwines::node(idx); }
void TwinePool::check_ready() { log_assert(StaticTwines::ready()); }
void TwinePool::canonicalize(TwineNode& t) {
if (auto *sfx = std::get_if<Twine::Suffix>(&t.data))
sfx->prefix = sfx->prefix.untag().stamped(0);
}
void TwinePool::canonicalize(TwineNode&) {}
size_t TwinePool::next_serial() {
static size_t counter = 0;
size_t serial = ++counter;
return serial > IdString::MAX_SERIAL ? (serial % IdString::MAX_SERIAL) + 1 : serial;
if constexpr (IdString::MAX_SERIAL == 0)
return serial;
else
return serial > IdString::MAX_SERIAL ? (serial % IdString::MAX_SERIAL) + 1 : serial;
}
IdString TwinePool::add_inner(TwineNode t) {
@@ -142,35 +208,32 @@ IdString TwinePool::add_inner(TwineNode t) {
}
size_t TwinePool::hash_node(const TwineNode& t) {
return hash_key(t.key());
}
size_t TwinePool::hash_key(const TwineNode::Key& k) {
Hasher h;
std::visit([&h](const auto& val) {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, Twine::Leaf>) {
h.eat(val.s);
} else if constexpr (std::is_same_v<T, Twine::Suffix>) {
h.eat(val.prefix);
h.eat(val.tail);
}
}, t.data);
if (k.prefix < TwineNode::DEAD)
h.eat(IdString(k.prefix));
h.eat(k.text);
return h.yield();
}
void TwinePool::dump(IdString ref, std::ostream& os) const {
const TwineNode& twine = (*this)[ref];
std::visit([&](const auto& val) {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, std::monostate>) {
os << "Dead()";
} else if constexpr (std::is_same_v<T, Twine::Leaf>) {
os << "Leaf(\"" << val.s << "\")";
} else if constexpr (std::is_same_v<T, Twine::Suffix>) {
os << "Suffix(prefix: ";
dump(val.prefix, os);
os << ", tail: \"" << val.tail << "\")";
}
}, twine.data);
switch (twine.kind()) {
case TwineNode::Kind::Dead:
os << "Dead()";
break;
case TwineNode::Kind::Leaf:
os << "Leaf(\"" << twine.text() << "\")";
break;
case TwineNode::Kind::Suffix:
os << "Suffix(prefix: ";
dump(twine.prefix(), os);
os << ", tail: \"" << twine.text() << "\")";
break;
}
if (ref.isPublic())
os << " pub";
}
@@ -180,16 +243,17 @@ void TwinePool::print(IdString ref, std::ostream& os) const {
return;
if (ref.isPublic())
os << '\\';
std::visit([&](const auto& val) {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, std::monostate>) {
} else if constexpr (std::is_same_v<T, Twine::Leaf>) {
os << val.s;
} else if constexpr (std::is_same_v<T, Twine::Suffix>) {
print(val.prefix, os);
os << val.tail;
}
}, (*this)[ref].data);
const TwineNode& twine = (*this)[ref];
switch (twine.kind()) {
case TwineNode::Kind::Dead:
break;
case TwineNode::Kind::Suffix:
print(twine.prefix(), os);
[[fallthrough]];
case TwineNode::Kind::Leaf:
os << twine.text();
break;
}
}
void TwinePool::append_str(IdString ref, std::string& out) const {
@@ -197,16 +261,17 @@ void TwinePool::append_str(IdString ref, std::string& out) const {
return;
if (ref.isPublic())
out += '\\';
std::visit([&](const auto& val) {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, std::monostate>) {
} else if constexpr (std::is_same_v<T, Twine::Leaf>) {
out += val.s;
} else if constexpr (std::is_same_v<T, Twine::Suffix>) {
append_str(val.prefix, out);
out += val.tail;
}
}, (*this)[ref].data);
const TwineNode& twine = (*this)[ref];
switch (twine.kind()) {
case TwineNode::Kind::Dead:
break;
case TwineNode::Kind::Suffix:
append_str(twine.prefix(), out);
[[fallthrough]];
case TwineNode::Kind::Leaf:
out += twine.text();
break;
}
}
std::string TwinePool::str(IdString ref) const {
@@ -219,36 +284,67 @@ std::string TwinePool::unescaped_str(IdString ref) const {
return str(ref.untag());
}
IdString TwinePool::find_content(uint32_t prefix, std::string_view text) const {
return HashConsPool::find_key(TwineNode::Key{prefix, text});
}
IdString TwinePool::intern(uint32_t prefix, std::string_view text) {
IdString ref = HashConsPool::find_key(TwineNode::Key{prefix, text});
if (ref != IdString::Null)
return ref;
return add_inner(TwineNode{prefix, text});
}
IdString TwinePool::find(const std::string &name) const {
bool is_public = !name.empty() && name[0] == '\\';
return stamp(find(Twine::Leaf{is_public ? name.substr(1) : name}).tag(is_public));
std::string_view content = name;
if (is_public)
content.remove_prefix(1);
return stamp(find_content(TwineNode::NO_PREFIX, content).tag(is_public));
}
IdString TwinePool::find(Twine t) const {
if (auto *ap = std::get_if<Twine::AutoSuffix>(&t.data)) {
IdString prefix = HashConsPool::find(Twine::Leaf{std::string(ap->prefix)});
IdString TwinePool::find(TwineSpec t) const {
if (auto *ap = std::get_if<TwineSpec::AutoSuffix>(&t.data)) {
IdString prefix = find_content(TwineNode::NO_PREFIX, *ap->prefix);
if (prefix == IdString::Null)
return IdString::Null;
t = Twine::Suffix{prefix, std::move(ap->tail)};
return stamp(find_content((uint32_t)prefix.untag().raw(), ap->tail).tag(prefix.isPublic()));
}
bool is_public = inherits_publicity(t);
return stamp(HashConsPool::find(to_node(std::move(t))).tag(is_public));
if (auto *leaf = std::get_if<TwineSpec::Leaf>(&t.data))
return stamp(find_content(TwineNode::NO_PREFIX, leaf->s));
const TwineSpec::Suffix &sfx = std::get<TwineSpec::Suffix>(t.data);
return stamp(find_content((uint32_t)sfx.prefix.untag().raw(), sfx.tail).tag(sfx.prefix.isPublic()));
}
IdString TwinePool::add(Twine t) {
if (auto *ap = std::get_if<Twine::AutoSuffix>(&t.data)) {
IdString prefix = add_inner(Twine::Leaf{std::string(ap->prefix)});
t = Twine::Suffix{prefix, std::move(ap->tail)};
IdString TwinePool::add(TwineSpec t) {
if (auto *ap = std::get_if<TwineSpec::AutoSuffix>(&t.data)) {
IdString prefix = auto_prefix(ap->prefix);
return stamp(intern((uint32_t)prefix.untag().raw(), ap->tail).tag(prefix.isPublic()));
}
bool is_public = inherits_publicity(t);
return stamp(add_inner(to_node(std::move(t))).tag(is_public));
if (auto *leaf = std::get_if<TwineSpec::Leaf>(&t.data))
return stamp(intern(TwineNode::NO_PREFIX, leaf->s));
const TwineSpec::Suffix &sfx = std::get<TwineSpec::Suffix>(t.data);
return stamp(intern((uint32_t)sfx.prefix.untag().raw(), sfx.tail).tag(sfx.prefix.isPublic()));
}
IdString TwinePool::auto_prefix(const std::string *prefix) {
auto it = auto_prefixes.find(prefix);
if (it != auto_prefixes.end())
return it->second;
IdString ref = intern(TwineNode::NO_PREFIX, *prefix);
auto_prefixes[prefix] = ref;
return ref;
}
IdString TwinePool::add(IdString prefix, std::string_view tail) {
return stamp(intern((uint32_t)prefix.untag().raw(), tail).tag(prefix.isPublic()));
}
IdString TwinePool::add(std::string s) {
if (s.empty())
return IdString::Null;
auto [content, is_public] = twine_unescape(std::move(s));
return stamp(add_inner(Twine::Leaf{std::move(content)}).tag(is_public));
return stamp(intern(TwineNode::NO_PREFIX, content).tag(is_public));
}
IdString TwinePool::copy_from(const TwinePool& src, IdString ref) {
@@ -260,10 +356,16 @@ IdString TwinePool::copy_from(const TwinePool& src, IdString ref) {
if (ID::is_static(untagged))
return ref;
const TwineNode& t = src[untagged];
if (t.is_leaf())
return (add(Twine::Leaf{t.leaf()})).tag(is_public);
if (t.is_suffix())
return (add(Twine::Suffix{copy_from(src, t.suffix().prefix), t.suffix().tail})).tag(is_public);
switch (t.kind()) {
case TwineNode::Kind::Leaf:
return stamp(intern(TwineNode::NO_PREFIX, t.text()).tag(is_public));
case TwineNode::Kind::Suffix: {
IdString prefix = copy_from(src, t.prefix());
return stamp(intern((uint32_t)prefix.untag().raw(), t.text()).tag(is_public));
}
case TwineNode::Kind::Dead:
break;
}
return IdString::Null;
}
@@ -276,32 +378,47 @@ IdString TwinePool::find_from(const TwinePool& src, IdString ref) const {
if (ID::is_static(untagged))
return ref;
const TwineNode& t = src[untagged];
if (t.is_leaf())
return find(Twine::Leaf{t.leaf()}).tag(is_public);
if (t.is_suffix()) {
IdString prefix = find_from(src, t.suffix().prefix);
switch (t.kind()) {
case TwineNode::Kind::Leaf:
return stamp(find_content(TwineNode::NO_PREFIX, t.text()).tag(is_public));
case TwineNode::Kind::Suffix: {
IdString prefix = find_from(src, t.prefix());
if (prefix == IdString::Null)
return IdString::Null;
return find(Twine::Suffix{prefix, t.suffix().tail}).tag(is_public);
return stamp(find_content((uint32_t)prefix.untag().raw(), t.text()).tag(is_public));
}
case TwineNode::Kind::Dead:
break;
}
return IdString::Null;
}
std::string TwinePool::ref_token(IdString ref) const {
return "#" + std::to_string((uint64_t)stamp(ref).bits());
return "#" + std::to_string((uint64_t)serial_) + ":"
+ std::to_string((uint64_t)stamp(ref).bits());
}
IdString TwinePool::ref_from_token(std::string_view token) const {
if (token.size() < 2 || token[0] != '#')
return IdString::Null;
size_t value = 0;
for (char c : token.substr(1)) {
if (c < '0' || c > '9')
size_t sep = token.find(':');
if (sep == std::string_view::npos)
return IdString::Null;
size_t fields[2] = {0, 0};
std::string_view parts[2] = {token.substr(1, sep - 1), token.substr(sep + 1)};
for (int i = 0; i < 2; i++) {
if (parts[i].empty())
return IdString::Null;
value = value * 10 + (c - '0');
for (char c : parts[i]) {
if (c < '0' || c > '9')
return IdString::Null;
fields[i] = fields[i] * 10 + (c - '0');
}
}
IdString ref(value);
if (ref == IdString::Null || (!ID::is_static(ref) && ref.serial() != serial_))
if (fields[0] != serial_)
return IdString::Null;
IdString ref(fields[1]);
if (ref == IdString::Null)
return IdString::Null;
return is_live(ref) ? ref : IdString::Null;
}
@@ -317,18 +434,7 @@ void TwinePool::dump(std::ostream& os) const {
os << "--------------------------------\n";
}
bool TwinePool::inherits_publicity(const Twine &t) {
auto *sfx = std::get_if<Twine::Suffix>(&t.data);
return sfx != nullptr && sfx->prefix.isPublic();
}
TwineNode TwinePool::to_node(Twine t) {
if (auto *leaf = std::get_if<Twine::Leaf>(&t.data))
return TwineNode{std::move(*leaf)};
return TwineNode{std::move(std::get<Twine::Suffix>(t.data))};
}
/**
/**
* TwineSegments holds a sequence of string_views refering to the strings
* an IdString is composed of. It's used for lightweight comparisons.
* The sequences is implemented in a "small vector" style,
@@ -358,13 +464,17 @@ TwineSegments::TwineSegments(const TwinePool &pool, IdString ref)
return;
for (IdString cur = ref.untag(); ;) {
const TwineNode &t = pool[cur];
if (t.is_suffix()) {
push(t.suffix().tail);
cur = t.suffix().prefix.untag();
switch (t.kind()) {
case TwineNode::Kind::Suffix:
push(t.text());
cur = t.prefix();
continue;
case TwineNode::Kind::Leaf:
push(t.text());
break;
case TwineNode::Kind::Dead:
break;
}
if (t.is_leaf())
push(t.leaf());
break;
}
if (ref.isPublic())
@@ -458,6 +568,13 @@ bool TwinePool::content_equal(IdString a, IdString b) const
return compare_segments(sa, sb) == 0;
}
IdString TwinePool::prefix_of(IdString ref) const {
const TwineNode &t = (*this)[ref];
if (!t.is_suffix())
return IdString::Null;
return stamp(t.prefix().tag(ref.isPublic()));
}
int TwinePool::compare_by_name(IdString a, IdString b) const
{
if (a == b)
@@ -471,9 +588,9 @@ int TwinePool::compare_by_name(IdString a, IdString b) const
const TwineNode &ta = (*this)[a.untag()];
const TwineNode &tb = (*this)[b.untag()];
if (ta.is_leaf() && tb.is_leaf())
return ta.leaf().compare(tb.leaf());
if (ta.is_suffix() && tb.is_suffix() && ta.suffix().prefix == tb.suffix().prefix)
return ta.suffix().tail.compare(tb.suffix().tail);
return ta.text().compare(tb.text());
if (ta.is_suffix() && tb.is_suffix() && ta.prefix() == tb.prefix())
return ta.text().compare(tb.text());
}
TwineSegments sa(*this, a), sb(*this, b);
@@ -500,13 +617,15 @@ void DeepTwineHash::combine(Stream& s, IdString t) const {
if (t == IdString::Null)
return;
const TwineNode& n = (*pool)[t];
if (n.is_dead()) return;
if (n.is_leaf()) {
s.push(n.leaf());
} else if (n.is_suffix()) {
combine(s, n.suffix().prefix);
s.push(n.suffix().tail);
switch (n.kind()) {
case TwineNode::Kind::Dead:
break;
case TwineNode::Kind::Suffix:
combine(s, n.prefix());
[[fallthrough]];
case TwineNode::Kind::Leaf:
s.push(n.text());
break;
}
}
@@ -526,16 +645,15 @@ bool DeepTwineEq::consume(IdString t, std::string_view& sv) const noexcept {
if (t == IdString::Null)
return true;
const TwineNode& n = (*pool)[t];
if (n.is_dead()) return true;
if (n.is_leaf()) {
if (!sv.starts_with(n.leaf())) return false;
sv.remove_prefix(n.leaf().size());
switch (n.kind()) {
case TwineNode::Kind::Dead:
return true;
} else if (n.is_suffix()) {
if (!consume(n.suffix().prefix, sv)) return false;
if (!sv.starts_with(n.suffix().tail)) return false;
sv.remove_prefix(n.suffix().tail.size());
case TwineNode::Kind::Suffix:
if (!consume(n.prefix(), sv)) return false;
[[fallthrough]];
case TwineNode::Kind::Leaf:
if (!sv.starts_with(n.text())) return false;
sv.remove_prefix(n.text().size());
return true;
}
return false;
+168 -55
View File
@@ -5,8 +5,11 @@
#include "kernel/yosys_common.h"
#include <algorithm>
#include <bit>
#include <cstdint>
#include <cstring>
#include <deque>
#include <limits>
#include <span>
#include <string>
#include <string_view>
@@ -16,27 +19,43 @@
YOSYS_NAMESPACE_BEGIN
struct Twine;
struct TwineSpec;
struct TwinePool;
struct IdString;
// An IdString now references a Twine in a per-Design TwinePool.
// Twines are designed to deduplicate prefixes if appropriately constructed.
// Statically, compile-time allocated indices defined in kernel/constids.inc
// are handled with a global read-only pool.
// A general IdString can't be constructed, converted to a string, or printed
// without a pointer to the pool.
// Two IdStrings that resolve to the same string won't generally compare
// equal with operator== but alternate mechanisms are provided for cases
// where that is absolutely necessary but it should be avoided for performance,
// see DeepTwine and TwineSearch.
// Twines are deduplicated within a TwinePool in a "shallow" way, similar to FRAIGing.
// Lookup from a string to IdString is expensive and provided by TwineSearch.
// Instead of backslash escaping, identifier "publicity" is implemented
// with a publicity bit on the IdString reference integer.
// TwinePool is backed by an std::deque and a free list
// and provides stable indices outside of garbage collection.
// "Content" refers to the untagged backing.
/**
* A twine is a data structure designed to deduplicate prefixes.
* The key idea is that a twine is a suffix node or a leaf node.
* A leaf node holds a string,
* while a suffix node appends a string to a twine.
*
* Twines are used here to implement IdString, the Yosys interned
* string type. They're extended to the needs of Yosys, specifically
* with efficient static NEW_ID prefixes,
* and IdString holding a special bit marking the publicity of a name.
* When public, a backslash is prepended when printing an IdString.
*
* An IdString references a TwineNode in a per-Design TwinePool
* by indexing into it. It is constructed by interning a TwineSpec
* into a TwinePool.
*
* A general IdString can't be constructed, converted to a string, or printed
* without a pointer to the TwinePool.
* Compile-time allocated indices defined in kernel/constids.inc
* live in a global read-only pool, so they're an exception to that.
*
* Two IdStrings that resolve to the same string won't generally compare
* equal with operator== so that comparisons are cheap,
* but alternate mechanisms are provided for cases
* where that is absolutely necessary but it should be avoided for performance,
* like DeepTwine and TwineSearch.
* Twines are deduplicated within a TwinePool in a "shallow" way, similar to FRAIGing.
* Lookup from a string to IdString is expensive and provided by TwineSearch.
*
* TwinePool is backed by an std::deque and a free list
* and provides stable indices outside of garbage collection.
* "Content" refers to the untagged backing.
*/
struct NullIdString {
constexpr operator IdString() const;
@@ -45,13 +64,20 @@ struct NullIdString {
struct IdString {
private:
size_t value;
#ifdef YOSYS_ENABLE_TWINE_PROVENANCE
using bits_t = uint64_t;
#else
using bits_t = uint32_t;
#endif
bits_t value;
static constexpr size_t TWINE_PUBLIC_BIT = 1ULL << 63;
static constexpr size_t TWINE_NULL_VAL = ~size_t{0};
static constexpr int TWINE_SERIAL_SHIFT = 32;
static constexpr size_t TWINE_INDEX_MASK = (size_t{1} << TWINE_SERIAL_SHIFT) - 1;
static constexpr size_t TWINE_SERIAL_MASK = ~(TWINE_INDEX_MASK | TWINE_PUBLIC_BIT);
static constexpr int TWINE_WIDTH = std::numeric_limits<bits_t>::digits;
static constexpr int TWINE_PUBLIC_SHIFT = TWINE_WIDTH - 1;
static constexpr bits_t TWINE_PUBLIC_BIT = bits_t{1} << TWINE_PUBLIC_SHIFT;
static constexpr bits_t TWINE_NULL_VAL = ~bits_t{0};
static constexpr int TWINE_SERIAL_SHIFT = TWINE_WIDTH == 64 ? 32 : TWINE_PUBLIC_SHIFT;
static constexpr bits_t TWINE_INDEX_MASK = (bits_t{1} << TWINE_SERIAL_SHIFT) - 1;
static constexpr bits_t TWINE_SERIAL_MASK = bits_t(~(TWINE_INDEX_MASK | TWINE_PUBLIC_BIT));
public:
static constexpr NullIdString Null{};
@@ -66,25 +92,27 @@ public:
// raw() with the pool serial kept
constexpr size_t bits() const { return value; }
constexpr size_t order_key() const { return std::rotl((bits_t) eq_key(), 1); }
constexpr size_t serial() const {
return value == TWINE_NULL_VAL ? 0 : ((value & TWINE_SERIAL_MASK) >> TWINE_SERIAL_SHIFT);
}
constexpr IdString stamped(size_t pool_serial) const {
return value == TWINE_NULL_VAL ? *this
: IdString((value & ~TWINE_SERIAL_MASK) | (pool_serial << TWINE_SERIAL_SHIFT));
: IdString((value & ~TWINE_SERIAL_MASK)
| (bits_t(pool_serial << TWINE_SERIAL_SHIFT) & TWINE_SERIAL_MASK));
}
constexpr IdString() : value(TWINE_NULL_VAL) {}
explicit constexpr IdString(size_t val) : value(val) {}
explicit constexpr IdString(size_t val) : value((bits_t) val) {}
constexpr bool operator==(const IdString &rhs) const { return raw() == rhs.raw(); }
constexpr bool operator!=(const IdString &rhs) const { return raw() != rhs.raw(); }
constexpr size_t eq_key() const { return value & ~TWINE_SERIAL_MASK; }
constexpr bool operator==(const IdString &rhs) const { return eq_key() == rhs.eq_key(); }
constexpr bool operator!=(const IdString &rhs) const { return eq_key() != rhs.eq_key(); }
constexpr std::strong_ordering operator<=>(const IdString &rhs) const {
size_t a = raw(), b = rhs.raw();
if (auto cmp = (a & ~TWINE_PUBLIC_BIT) <=> (b & ~TWINE_PUBLIC_BIT); cmp != 0)
return cmp;
return (a & TWINE_PUBLIC_BIT) <=> (b & TWINE_PUBLIC_BIT);
return order_key() <=> rhs.order_key();
}
template <typename... Args>
@@ -121,6 +149,22 @@ public:
constexpr NullIdString::operator IdString() const { return IdString(); }
constexpr bool NullIdString::operator==(IdString ref) const { return ref.empty(); }
namespace hashlib {
template<>
struct hash_ops<IdString> {
static inline bool cmp(IdString a, IdString b) { return a == b; }
[[nodiscard]] static inline Hasher hash(IdString id) {
Hasher h;
h.force((Hasher::hash_t) id.bits());
return h;
}
[[nodiscard]] static inline Hasher hash_into(IdString id, Hasher h) {
h.hash64(id.eq_key());
return h;
}
};
}
enum : short {
// STATIC_TWINE_BEGIN = 0,
@@ -173,9 +217,9 @@ template<size_t Raw> inline constexpr IdString constid = IdString(Raw);
#define ID(id) (YOSYS_NAMESPACE_PREFIX constid<YOSYS_NAMESPACE_PREFIX ID::lookup(#id).raw()>)
// Twine is the object that lives in the TwinePool,
// while IdString points to a Twine and carries publicity information
struct Twine {
// TwineSpec is the object that lives in the TwinePool,
// while IdString points to a TwineSpec and carries publicity information
struct TwineSpec {
// deduplicates shared prefixes
struct Suffix {
IdString prefix;
@@ -186,7 +230,7 @@ struct Twine {
// transient suffix constructed with NEW_ID and NEW_ID_SUFFIX
// turned into a regular Suffix when added to a TwinePool
struct AutoSuffix {
std::string_view prefix;
const std::string *prefix;
std::string tail;
auto operator<=>(const AutoSuffix&) const = default;
};
@@ -199,30 +243,88 @@ struct Twine {
std::variant<Leaf, Suffix, AutoSuffix> data;
Twine(Leaf v);
Twine(Suffix v);
Twine(AutoSuffix v);
TwineSpec(Leaf v);
TwineSpec(Suffix v);
TwineSpec(AutoSuffix v);
bool is_leaf() const;
bool is_suffix() const;
bool holds_leaf() const;
bool holds_suffix() const;
// Only for the pool-free variants, Leaf and AutoSuffix
std::string content_str() const;
};
struct SmallString {
static constexpr uint32_t INLINE_CAP = 8;
static constexpr size_t MAX_LEN = std::numeric_limits<uint32_t>::max();
SmallString() = default;
explicit SmallString(std::string_view content);
SmallString(const SmallString &other);
SmallString(SmallString &&other) noexcept;
SmallString &operator=(const SmallString &other);
SmallString &operator=(SmallString &&other) noexcept;
~SmallString();
std::string_view view() const { return {len_ <= INLINE_CAP ? inl_ : ptr_, len_}; }
private:
void store(std::string_view content);
void release();
union {
char inl_[INLINE_CAP];
char *ptr_;
};
uint32_t len_ = 0;
};
// TwineNode is the in-memory version of TwineSpec
struct TwineNode {
std::variant<std::monostate, Twine::Leaf, Twine::Suffix> data;
static constexpr uint32_t NO_PREFIX = ~uint32_t{0};
static constexpr uint32_t DEAD = NO_PREFIX - 1;
struct Key {
uint32_t prefix;
std::string_view text;
};
TwineNode() = default;
TwineNode(Twine::Leaf v);
TwineNode(Twine::Suffix v);
explicit TwineNode(std::string_view content) : text_(content), prefix_(NO_PREFIX) {}
TwineNode(uint32_t prefix, std::string_view tail) : text_(tail), prefix_(prefix) {}
TwineNode(const TwineNode &other) = default;
TwineNode(TwineNode &&other) noexcept;
TwineNode &operator=(const TwineNode &other) = default;
TwineNode &operator=(TwineNode &&other) noexcept;
~TwineNode() = default;
bool is_dead() const;
bool is_leaf() const;
bool is_suffix() const;
const std::string &leaf() const;
const Twine::Suffix &suffix() const;
enum class Kind { Dead, Leaf, Suffix };
constexpr bool is_dead() const { return prefix_ == DEAD; }
constexpr bool is_leaf() const { return prefix_ == NO_PREFIX; }
constexpr bool is_suffix() const { return prefix_ < DEAD; }
constexpr Kind kind() const {
return prefix_ == DEAD ? Kind::Dead
: prefix_ == NO_PREFIX ? Kind::Leaf
: Kind::Suffix;
}
std::string_view text() const { return text_.view(); }
IdString prefix() const { return IdString(prefix_); }
Key key() const { return {prefix_, text()}; }
bool operator==(const TwineNode &other) const
{ return prefix_ == other.prefix_ && text() == other.text(); }
bool operator==(const Key &other) const
{ return prefix_ == other.prefix && text() == other.text; }
private:
YS_NO_UNIQUE_ADDRESS SmallString text_;
uint32_t prefix_ = DEAD;
};
static_assert(sizeof(TwineNode) == 16);
struct StaticTwines {
static constexpr size_t count = STATIC_TWINE_END;
@@ -260,11 +362,19 @@ struct TwinePool : HashConsPool<TwinePool, TwineNode, IdString> {
static void check_ready();
static void canonicalize(TwineNode& t);
static size_t hash_node(const TwineNode& t);
static size_t hash_key(const TwineNode::Key& k);
template<typename F>
static void for_each_child(const TwineNode& t, F&& f) {
if (t.is_suffix())
f(t.suffix().prefix);
f(t.prefix());
}
template<typename Roots>
size_t gc(Roots& roots) {
for (auto &it : auto_prefixes)
roots.insert(it.second);
return HashConsPool::gc(roots);
}
void dump(IdString ref, std::ostream& os = std::cout) const;
@@ -282,12 +392,15 @@ struct TwinePool : HashConsPool<TwinePool, TwineNode, IdString> {
bool name_equal(IdString ref, std::string_view name) const;
bool content_equal(IdString a, IdString b) const;
int compare_by_name(IdString a, IdString b) const;
IdString prefix_of(IdString ref) const;
// Only finds leaves. For compatibility only
IdString find(const std::string &name) const;
IdString find(Twine t) const;
IdString find(TwineSpec t) const;
// Doesn't infer publicity
IdString add(Twine t);
IdString add(TwineSpec t);
IdString add(IdString prefix, std::string_view tail);
IdString auto_prefix(const std::string *prefix);
// Infers publicity from first character
IdString add(std::string s);
IdString copy_from(const TwinePool& src, IdString ref);
@@ -303,11 +416,11 @@ struct TwinePool : HashConsPool<TwinePool, TwineNode, IdString> {
private:
IdString add_inner(TwineNode t);
IdString intern(uint32_t prefix, std::string_view text);
IdString find_content(uint32_t prefix, std::string_view text) const;
static size_t next_serial();
dict<const std::string *, IdString> auto_prefixes;
size_t serial_;
static bool inherits_publicity(const Twine &t);
static TwineNode to_node(Twine t);
};
/**
+10 -4
View File
@@ -123,6 +123,12 @@
# define YS_MAYBE_UNUSED
#endif
#if defined(_MSC_VER)
# define YS_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#else
# define YS_NO_UNIQUE_ADDRESS [[no_unique_address]]
#endif
#if __cplusplus >= 202002L
# define YS_FALLTHROUGH [[fallthrough]];
#else
@@ -309,14 +315,14 @@ extern bool yosys_write_versions;
const std::string *create_id_prefix(std::string_view file, int line, std::string_view func);
#define NEW_ID \
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> std::string_view { \
YOSYS_NAMESPACE_PREFIX TwineSpec{YOSYS_NAMESPACE_PREFIX TwineSpec::AutoSuffix{[](std::string_view func) -> const std::string * { \
static std::unique_ptr<const std::string> prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \
return *prefix; \
return prefix.get(); \
}(__FUNCTION__), std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}}
#define NEW_ID_SUFFIX(suffix) \
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> std::string_view { \
YOSYS_NAMESPACE_PREFIX TwineSpec{YOSYS_NAMESPACE_PREFIX TwineSpec::AutoSuffix{[](std::string_view func) -> const std::string * { \
static std::unique_ptr<const std::string> prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \
return *prefix; \
return prefix.get(); \
}(__FUNCTION__), std::string(suffix) + "$" + std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}}
YOSYS_NAMESPACE_END
+1
View File
@@ -18,5 +18,6 @@
#cmakedefine YOSYS_ENABLE_TCL
#cmakedefine YOSYS_ENABLE_PYTHON
#cmakedefine YOSYS_ENABLE_VERIFIC
#cmakedefine YOSYS_ENABLE_TWINE_PROVENANCE
#endif
+1 -1
View File
@@ -297,7 +297,7 @@ struct DesignPass : public Pass {
if (done.count(cell->type) == 0)
{
IdString trg_ref = copy_to_design->twines.add(
Twine::Suffix{as_name_ref, "." + cell->type.unescape()});
TwineSpec::Suffix{as_name_ref, "." + cell->type.unescape()});
log("Importing %s as %s.\n", fmod, PooledName(copy_to_design, trg_ref).unescape());
+9 -9
View File
@@ -834,7 +834,7 @@ struct DftTagWorker {
}
SigSpec autoAnd(Twine &&name, const SigSpec &sig_a, const SigSpec &sig_b)
SigSpec autoAnd(TwineSpec &&name, const SigSpec &sig_a, const SigSpec &sig_b)
{
log_assert(GetSize(sig_a) == GetSize(sig_b));
if (sig_a.is_fully_zero() || sig_b.is_fully_ones() || sig_a == sig_b)
@@ -845,7 +845,7 @@ struct DftTagWorker {
return module->And(std::move(name), sig_a, sig_b);
}
SigSpec autoOr(Twine &&name, const SigSpec &sig_a, const SigSpec &sig_b)
SigSpec autoOr(TwineSpec &&name, const SigSpec &sig_a, const SigSpec &sig_b)
{
log_assert(GetSize(sig_a) == GetSize(sig_b));
if (sig_a.is_fully_ones() || sig_b.is_fully_zero() || sig_a == sig_b)
@@ -856,7 +856,7 @@ struct DftTagWorker {
return module->Or(std::move(name), sig_a, sig_b);
}
SigSpec autoXor(Twine &&name, const SigSpec &sig_a, const SigSpec &sig_b)
SigSpec autoXor(TwineSpec &&name, const SigSpec &sig_a, const SigSpec &sig_b)
{
log_assert(GetSize(sig_a) == GetSize(sig_b));
if (sig_a == sig_b)
@@ -872,7 +872,7 @@ struct DftTagWorker {
return module->Xor(std::move(name), sig_a, sig_b);
}
SigSpec autoXnor(Twine &&name, const SigSpec &sig_a, const SigSpec &sig_b)
SigSpec autoXnor(TwineSpec &&name, const SigSpec &sig_a, const SigSpec &sig_b)
{
log_assert(GetSize(sig_a) == GetSize(sig_b));
if (sig_a == sig_b)
@@ -888,7 +888,7 @@ struct DftTagWorker {
return module->Xnor(std::move(name), sig_a, sig_b);
}
SigSpec autoNot(Twine &&name, const SigSpec &sig_a)
SigSpec autoNot(TwineSpec &&name, const SigSpec &sig_a)
{
if (sig_a.is_fully_const()) {
auto const_val = sig_a.as_const();
@@ -901,7 +901,7 @@ struct DftTagWorker {
return module->Not(std::move(name), sig_a);
}
SigSpec autoEq(Twine &&name, const SigSpec &sig_a, const SigSpec &sig_b)
SigSpec autoEq(TwineSpec &&name, const SigSpec &sig_a, const SigSpec &sig_b)
{
log_assert(GetSize(sig_a) == GetSize(sig_b));
if (sig_a == sig_b)
@@ -919,7 +919,7 @@ struct DftTagWorker {
return module->Eq(std::move(name), sig_a, sig_b);
}
SigSpec autoGe(Twine &&name, const SigSpec &sig_a, const SigSpec &sig_b)
SigSpec autoGe(TwineSpec &&name, const SigSpec &sig_a, const SigSpec &sig_b)
{
log_assert(GetSize(sig_a) == GetSize(sig_b));
if (sig_a == sig_b || sig_a.is_fully_ones())
@@ -930,7 +930,7 @@ struct DftTagWorker {
return module->Ge(std::move(name), sig_a, sig_b);
}
SigSpec autoReduceAnd(Twine &&name, const SigSpec &sig_a)
SigSpec autoReduceAnd(TwineSpec &&name, const SigSpec &sig_a)
{
if (GetSize(sig_a) == 0)
return State::S1;
@@ -945,7 +945,7 @@ struct DftTagWorker {
return module->ReduceAnd(std::move(name), sig_a);
}
SigSpec autoReduceOr(Twine &&name, const SigSpec &sig_a)
SigSpec autoReduceOr(TwineSpec &&name, const SigSpec &sig_a)
{
if (GetSize(sig_a) == 0)
return State::S0;
+7 -7
View File
@@ -72,21 +72,21 @@ IdString remap_flattened_name(RTLIL::Design *design, IdString obj_ref,
return it->second;
const TwineNode &node = design->twines[obj_ref];
IdString parent = design->twines.prefix_of(obj_ref);
IdString result;
if (node.is_suffix()) {
const Twine::Suffix &sfx = node.suffix();
IdString prefix = remap_flattened_name(design, sfx.prefix.tag(obj_ref.isPublic()),
if (parent != IdString::Null) {
IdString prefix = remap_flattened_name(design, parent,
pub_prefix_ref, priv_prefix_ref, separator, memo);
result = design->twines.add(Twine::Suffix{prefix, sfx.tail});
result = design->twines.add(TwineSpec::Suffix{prefix, std::string(node.text())});
} else {
std::string_view obj = node.leaf();
std::string_view obj = node.text();
if (obj_ref.isPublic()) {
result = design->twines.add(Twine::Suffix{pub_prefix_ref, separator + std::string(obj)});
result = design->twines.add(TwineSpec::Suffix{pub_prefix_ref, separator + std::string(obj)});
} 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::Suffix{priv_prefix_ref, std::string(obj)});
result = design->twines.add(TwineSpec::Suffix{priv_prefix_ref, std::string(obj)});
}
}
memo[obj_ref] = result;
+1 -1
View File
@@ -49,7 +49,7 @@ inline IdString ref_from_token(RTLIL::Design *design, const std::string &tok)
inline IdString indexed_name(RTLIL::Design *design, IdString base, int index)
{
return design->twines.add(Twine::Suffix{base.untag(), stringf("[%d]", index)})
return design->twines.add(TwineSpec::Suffix{base.untag(), stringf("[%d]", index)})
.tag(base.isPublic());
}
+1 -1
View File
@@ -201,7 +201,7 @@ struct BoothPassWorker {
log_assert(sig_a.size() == sig_y.size());
for (int i = 0; i < sig_a.size(); i++)
mod->addFa(Twine::Suffix{name, stringf("[%d]", i)}, sig_a[i], sig_b[i],
mod->addFa(TwineSpec::Suffix{name, stringf("[%d]", i)}, sig_a[i], sig_b[i],
sig_c[i], sig_x[i], sig_y[i]);
}
+10 -10
View File
@@ -59,7 +59,7 @@ struct PrefixApplier
PrefixApplier(RTLIL::Design *dst, IdString prefix, RTLIL::Design *src)
: dst(dst), src(src), cell_name(prefix)
{
pub_prefix = dst->twines.add(Twine::Suffix{prefix, "."});
pub_prefix = dst->twines.add(TwineSpec::Suffix{prefix, "."});
}
IdString techmap_prefix()
@@ -75,14 +75,14 @@ struct PrefixApplier
return it->second;
const TwineNode &node = src->twines[obj_ref];
IdString parent = src->twines.prefix_of(obj_ref);
IdString result;
if (node.is_suffix()) {
const Twine::Suffix &sfx = node.suffix();
IdString prefix = name(sfx.prefix.tag(obj_ref.isPublic()));
result = dst->twines.add(Twine::Suffix{prefix, sfx.tail});
if (parent != IdString::Null) {
IdString prefix = name(parent);
result = dst->twines.add(prefix, node.text());
} else {
IdString prefix = obj_ref.isPublic() ? pub_prefix : techmap_prefix();
result = dst->twines.add(Twine::Suffix{prefix, node.leaf()});
result = dst->twines.add(prefix, node.text());
}
memo[obj_ref] = result;
return result;
@@ -103,7 +103,7 @@ struct PrefixApplier
static RTLIL::Wire *map_port(RTLIL::Module *tpl, RTLIL::Design *src, IdString name)
{
return tpl->wire(tpl->design->twines.find(src->twines.str(name)));
return tpl->wire(tpl->design->twines.find_from(src->twines, name));
}
struct TechmapWorker
@@ -165,7 +165,7 @@ struct TechmapWorker
return result;
for (auto w : module->wires()) {
if (w->name.str()[0] == '$')
if (!w->name.isPublic())
continue;
if (w->name.contains("_TECHMAP_") && !w->name.contains("_TECHMAP_REPLACE_")) {
@@ -239,7 +239,7 @@ struct TechmapWorker
IdString posportref = module->design->twines.add(std::string{stringf("$%d", tpl_w->port_id)});
positional_ports.emplace(posportref, tpl_w->name);
IdString tpl_portname = module->design->twines.find(tpl_w->name.str());
IdString tpl_portname = module->design->twines.find_from(tpl->design->twines, tpl_w->name);
if (tpl_w->get_bool_attribute(ID::techmap_autopurge) &&
(!cell->hasPort(tpl_portname) || !GetSize(cell->getPort(tpl_portname))) &&
(!cell->hasPort(posportref) || !GetSize(cell->getPort(posportref))))
@@ -300,7 +300,7 @@ struct TechmapWorker
else
w = map_port(tpl, design, portname);
if (w == nullptr || w->port_id == 0) {
if (design->twines.str(portname).starts_with("$"))
if (!portname.isPublic())
log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", PooledName(design, portname).unescape(), cell->name.unescape(), tpl->name.unescape());
continue;
}
+1
View File
@@ -11,6 +11,7 @@ yosys_gtest(kernel
rtlilHelpers.h
rtlilStringTest.cc
rtlilTest.cc
sigbitPerfTest.cc
sigspecExtractTest.cc
sigspecRemove2Test.cc
threadingTest.cc
+1 -1
View File
@@ -46,7 +46,7 @@ TEST(NameMasqTest, SuffixQueries)
Design design;
Module *mod = design.addModule(std::string("\\zz_top"));
IdString prefix = design.twines.add(std::string("\\zz_bus"));
IdString suffixed = design.twines.add(Twine{Twine::Suffix{prefix, "_hi"}});
IdString suffixed = design.twines.add(TwineSpec{TwineSpec::Suffix{prefix, "_hi"}});
Wire *w = mod->addWire(suffixed, 1);
EXPECT_EQ(w->name.escaped(), "\\zz_bus_hi");
+158
View File
@@ -0,0 +1,158 @@
#include <gtest/gtest.h>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <sys/resource.h>
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN
namespace {
int env_int(const char *name, int fallback)
{
const char *value = getenv(name);
if (value == nullptr || *value == 0)
return fallback;
return atoi(value);
}
size_t peak_rss_bytes()
{
struct rusage ru;
if (getrusage(RUSAGE_SELF, &ru) != 0)
return 0;
return (size_t)ru.ru_maxrss * 1024;
}
double ms(std::chrono::steady_clock::duration d)
{
return std::chrono::duration<double, std::milli>(d).count();
}
uint32_t next_rand(uint32_t &state)
{
state = state * 1664525u + 1013904223u;
return state >> 8;
}
std::string padded(const char *stem, int index, int extra_chars)
{
std::string name = stringf("\\%s_%d", stem, index);
name += std::string(extra_chars, 'n');
return name;
}
}
TEST(SigBitPerf, hash_and_sigmap_stress)
{
int wire_count = env_int("YOSYS_SIGBIT_PERF_WIRES", 4000);
int wire_width = env_int("YOSYS_SIGBIT_PERF_WIDTH", 16);
int alias_every = env_int("YOSYS_SIGBIT_PERF_ALIAS", 4);
int probe_rounds = env_int("YOSYS_SIGBIT_PERF_ROUNDS", 20);
int name_padding = env_int("YOSYS_SIGBIT_PERF_NAMEPAD", 24);
ASSERT_GE(wire_count, 2);
ASSERT_GE(wire_width, 1);
ASSERT_GE(alias_every, 2);
ASSERT_GE(probe_rounds, 1);
RTLIL::Design *design = new RTLIL::Design;
RTLIL::Module *module = design->addModule(padded("sigbit_perf_module", 0, name_padding));
std::vector<RTLIL::Wire *> wires;
wires.reserve(wire_count);
for (int i = 0; i < wire_count; i++)
wires.push_back(module->addWire(padded("sigbit_perf_wire", i, name_padding), wire_width));
int aliased = 0;
for (int i = alias_every; i < wire_count; i += alias_every) {
module->connect(RTLIL::SigSpec(wires[i]), RTLIL::SigSpec(wires[i - 1]));
aliased++;
}
std::vector<RTLIL::SigBit> bits;
bits.reserve((size_t)wire_count * wire_width);
for (RTLIL::Wire *wire : wires)
for (int i = 0; i < wire_width; i++)
bits.push_back(RTLIL::SigBit(wire, i));
uint32_t rng = 12345;
std::vector<RTLIL::SigBit> probes = bits;
for (size_t i = probes.size(); i > 1; i--)
std::swap(probes[i - 1], probes[next_rand(rng) % i]);
size_t rss_before = peak_rss_bytes();
auto t0 = std::chrono::steady_clock::now();
SigMap sigmap(module);
auto t1 = std::chrono::steady_clock::now();
size_t sigmap_checksum = 0;
for (int round = 0; round < probe_rounds; round++)
for (const RTLIL::SigBit &bit : probes)
sigmap_checksum += sigmap(bit).offset;
auto t2 = std::chrono::steady_clock::now();
pool<RTLIL::SigBit> seen;
for (int round = 0; round < probe_rounds; round++) {
seen.clear();
for (const RTLIL::SigBit &bit : probes)
seen.insert(bit);
}
auto t3 = std::chrono::steady_clock::now();
size_t hits = 0;
for (int round = 0; round < probe_rounds; round++)
for (const RTLIL::SigBit &bit : probes)
hits += seen.count(bit);
auto t4 = std::chrono::steady_clock::now();
dict<RTLIL::SigBit, int> owner;
for (int round = 0; round < probe_rounds; round++) {
owner.clear();
int n = 0;
for (const RTLIL::SigBit &bit : probes)
owner[bit] = n++;
}
auto t5 = std::chrono::steady_clock::now();
size_t dict_checksum = 0;
for (int round = 0; round < probe_rounds; round++)
for (const RTLIL::SigBit &bit : probes)
dict_checksum += owner.at(bit);
auto t6 = std::chrono::steady_clock::now();
size_t bit_count = probes.size();
size_t ops = bit_count * (size_t)probe_rounds;
EXPECT_EQ(seen.size(), bit_count);
EXPECT_EQ(owner.size(), bit_count);
EXPECT_EQ(hits, ops);
printf("[ PERF ] wires=%d width=%d alias_every=%d aliased=%d namepad=%d rounds=%d\n",
wire_count, wire_width, alias_every, aliased, name_padding, probe_rounds);
printf("[ PERF ] bits=%zu ops_per_phase=%zu checksums=%zu,%zu\n",
bit_count, ops, sigmap_checksum, dict_checksum);
printf("[ PERF ] sigmap_build_ms=%.1f sigmap_lookup_ns=%.2f\n",
ms(t1 - t0), 1e6 * ms(t2 - t1) / (double)ops);
printf("[ PERF ] pool_insert_ns=%.2f pool_lookup_ns=%.2f\n",
1e6 * ms(t3 - t2) / (double)ops, 1e6 * ms(t4 - t3) / (double)ops);
printf("[ PERF ] dict_insert_ns=%.2f dict_lookup_ns=%.2f\n",
1e6 * ms(t5 - t4) / (double)ops, 1e6 * ms(t6 - t5) / (double)ops);
printf("[ PERF ] total_ms=%.1f peak_rss_mb=%.1f rss_before_mb=%.1f\n",
ms(t6 - t0), peak_rss_bytes() / 1048576.0, rss_before / 1048576.0);
fflush(stdout);
delete design;
}
YOSYS_NAMESPACE_END
+3 -3
View File
@@ -18,7 +18,7 @@ TEST(TwineHashTest, Fragmentation)
IdString flat = pool.add(std::string("$abcdefghij"));
IdString base = pool.add(std::string("$abcde"));
IdString split = pool.add(Twine::Suffix{base, "fghij"});
IdString split = pool.add(TwineSpec::Suffix{base, "fghij"});
EXPECT_EQ(hash(flat.untag()), hash(split.untag()));
EXPECT_EQ(hash(flat.untag()), hash(std::string_view("$abcdefghij")));
@@ -35,7 +35,7 @@ TEST(TwineHashTest, SplitPoints)
for (size_t cut = 1; cut < content.size(); cut++) {
IdString base = pool.add(content.substr(0, cut));
IdString split = pool.add(Twine::Suffix{base, content.substr(cut)});
IdString split = pool.add(TwineSpec::Suffix{base, content.substr(cut)});
EXPECT_EQ(hash(split.untag()), want) << "split after " << cut;
}
}
@@ -63,7 +63,7 @@ TEST(TwineHashTest, BenchmarkIntern)
std::vector<IdString> refs;
refs.reserve(kNames);
for (int i = 0; i < kNames; i++)
refs.push_back(pool.add(Twine::Suffix{prefix, stringf("$%d", i)}));
refs.push_back(pool.add(TwineSpec::Suffix{prefix, stringf("$%d", i)}));
auto t1 = std::chrono::steady_clock::now();
TwineSearch search(&pool);
+6 -6
View File
@@ -47,8 +47,8 @@ TEST(TwinePublicityTest, SuffixPublicity)
IdString pub = pool.add(std::string("\\base"));
IdString priv = pool.add(std::string("$base"));
IdString pub_sfx = pool.add(Twine{Twine::Suffix{pub, "_1"}});
IdString priv_sfx = pool.add(Twine{Twine::Suffix{priv, "_1"}});
IdString pub_sfx = pool.add(TwineSpec{TwineSpec::Suffix{pub, "_1"}});
IdString priv_sfx = pool.add(TwineSpec{TwineSpec::Suffix{priv, "_1"}});
EXPECT_TRUE(pub_sfx.isPublic());
EXPECT_FALSE(priv_sfx.isPublic());
@@ -82,9 +82,9 @@ TEST(TwinePublicityTest, LookupTag)
TEST(TwinePublicityTest, SearchUnifies)
{
TwinePool pool;
IdString flat = pool.add(Twine::Leaf{"$abc"});
IdString head = pool.add(Twine::Leaf{"$a"});
IdString split = pool.add(Twine::Suffix{head, "bc"});
IdString flat = pool.add(TwineSpec::Leaf{"$abc"});
IdString head = pool.add(TwineSpec::Leaf{"$a"});
IdString split = pool.add(TwineSpec::Suffix{head, "bc"});
ASSERT_NE(flat, split);
ASSERT_EQ(pool.str(flat), pool.str(split));
@@ -100,7 +100,7 @@ TEST(TwinePublicityTest, SearchUnifies)
TEST(TwinePublicityTest, SearchPublicity)
{
TwinePool pool;
IdString priv = pool.add(Twine::Leaf{"sig"});
IdString priv = pool.add(TwineSpec::Leaf{"sig"});
IdString pub = pool.add(std::string("\\sig"));
ASSERT_EQ(priv, pub.untag());
+3 -3
View File
@@ -15,7 +15,7 @@ std::vector<IdString> bench_refs(TwinePool &twines, int count)
std::vector<IdString> refs;
refs.reserve(count);
for (int i = 0; i < count; i++)
refs.push_back(twines.add(Twine::Suffix{prefix, stringf("$%08d", (count - i) * 7919 % count)}));
refs.push_back(twines.add(TwineSpec::Suffix{prefix, stringf("$%08d", (count - i) * 7919 % count)}));
return refs;
}
@@ -69,14 +69,14 @@ TEST(TwineSortTest, GeneralWalk)
IdString deep = a;
for (int i = 0; i < 12; i++) {
deep = twines.add(Twine::Suffix{deep, stringf(".lvl%d", i)});
deep = twines.add(TwineSpec::Suffix{deep, stringf(".lvl%d", i)});
refs.push_back(deep);
refs.push_back(deep.tag(true));
}
IdString other = twines.add(std::string("$alpha."));
for (int i = 0; i < 12; i++) {
other = twines.add(Twine::Suffix{other, stringf("lvl%d.", i)});
other = twines.add(TwineSpec::Suffix{other, stringf("lvl%d.", i)});
refs.push_back(other);
}
+1 -1
View File
@@ -28,4 +28,4 @@ hierarchy -top top
rename -seed 2 -scramble-name c:bar
select -assert-none c:bar
select -assert-count 1 c:$_*_
select -assert-none c:$_*_ w:* foo/c:$add$<inlined>:2$1 %% %n
select -assert-none c:$_*_ w:* foo/c:$<inlined>:2$add$1 %% %n
+2 -2
View File
@@ -27,7 +27,7 @@ select -assert-count 1 w:$e
# Colliding with keyword
select -assert-count 1 w:wire
# Don't touch internal names
select -assert-count 1 w:$add$<inlined>:*$1_Y
select -assert-count 1 w:$<inlined>:*$add$*_Y
rename -unescape
@@ -38,7 +38,7 @@ select -assert-count 1 w:d_
select -assert-count 1 w:d__1
select -assert-count 1 w:_e
select -assert-count 1 w:wire_
select -assert-count 1 w:$add$<inlined>:*$1_Y
select -assert-count 1 w:$<inlined>:*$add$*_Y
# Ports are updated during rename
design -reset