From 0f13b551731766214ba6bb839c2dbddfec0c7c8c Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 3 Apr 2025 13:11:48 +0200 Subject: [PATCH] Liberty file caching with new `libcache` command This adds optional in-memory caching of parsed liberty files to speed up flows that repeatedly parse the same liberty files. To avoid increasing the memory overhead by default, the caching is disabled by default. The caching can be controlled globally or on a per path basis using the new `libcache` command, which also allows purging cached data. --- frontends/liberty/liberty.cc | 2 +- passes/cmds/stat.cc | 2 +- passes/techmap/Makefile.inc | 1 + passes/techmap/clockgate.cc | 2 +- passes/techmap/dfflibmap.cc | 2 +- passes/techmap/libcache.cc | 130 +++++++++++++++++++++++++++++++++++ passes/techmap/libparse.cc | 25 +++++++ passes/techmap/libparse.h | 48 +++++++++---- tests/liberty/libcache.ys | 58 ++++++++++++++++ 9 files changed, 253 insertions(+), 17 deletions(-) create mode 100644 passes/techmap/libcache.cc create mode 100644 tests/liberty/libcache.ys diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index cda13ff8b..3228f02fb 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -539,7 +539,7 @@ struct LibertyFrontend : public Frontend { log_header(design, "Executing Liberty frontend: %s\n", filename.c_str()); - LibertyParser parser(*f); + LibertyParser parser(*f, filename); int cell_count = 0; std::map> global_type_map; diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 7e51b6cb1..23583a8a8 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -352,7 +352,7 @@ void read_liberty_cellarea(dict &cell_area, string libert yosys_input_files.insert(liberty_file); if (f->fail()) log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); - LibertyParser libparser(*f); + LibertyParser libparser(*f, liberty_file); delete f; for (auto cell : libparser.ast->children) diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 4e1d16744..e6a910a0d 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -6,6 +6,7 @@ OBJS += passes/techmap/dfflibmap.o OBJS += passes/techmap/maccmap.o OBJS += passes/techmap/booth.o OBJS += passes/techmap/libparse.o +OBJS += passes/techmap/libcache.o ifeq ($(ENABLE_ABC),1) OBJS += passes/techmap/abc.o diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index 363f998db..b34ad1769 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -312,7 +312,7 @@ struct ClockgatePass : public Pass { std::istream* f = uncompressed(path); if (f->fail()) log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno)); - LibertyParser p(*f); + LibertyParser p(*f, path); merged.merge(p); delete f; } diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index fe4f8aada..84db7f157 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -634,7 +634,7 @@ struct DfflibmapPass : public Pass { std::istream* f = uncompressed(path); if (f->fail()) log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno)); - LibertyParser p(*f); + LibertyParser p(*f, path); merged.merge(p); delete f; } diff --git a/passes/techmap/libcache.cc b/passes/techmap/libcache.cc new file mode 100644 index 000000000..19f1fa87d --- /dev/null +++ b/passes/techmap/libcache.cc @@ -0,0 +1,130 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + + #include "kernel/yosys.h" + #include "passes/techmap/libparse.h" + + USING_YOSYS_NAMESPACE + PRIVATE_NAMESPACE_BEGIN + + struct LibcachePass : public Pass { + LibcachePass() : Pass("libcache", "control caching of technology library data parsed from liberty files") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" libcache {-enable|-disable|-purge} { -all | [path]... }\n"); + log("\n"); + log("Controls the default and per path caching of liberty file data.\n"); + log("\n"); + log(" -enable Enable caching.\n"); + log(" -disable Disable caching.\n"); + log(" -purge Reset cache setting and forget cached data.\n"); + log("\n"); + log("This mode takes a list of paths as argument. If no paths are provided, this\n"); + log("command does nothing. The -all option can be used to change the default cache\n"); + log("setting for -enable/-disable or to reset and forget about all paths.\n"); + log("\n"); + log("By default caching is disabled.\n"); + log("\n"); + log(" libcache -list\n"); + log("\n"); + log("Displays the current cache settings and cached paths.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *) override + { + bool enable = false; + bool disable = false; + bool purge = false; + bool all = false; + bool list = false; + std::vector paths; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-enable") { + enable = true; + continue; + } + if (args[argidx] == "-disable") { + enable = true; + continue; + } + if (args[argidx] == "-purge") { + purge = true; + continue; + } + if (args[argidx] == "-all") { + all = true; + continue; + } + if (args[argidx] == "-list") { + list = true; + continue; + } + std::string fname = args[argidx]; + rewrite_filename(fname); + paths.push_back(fname); + break; + } + int modes = enable + disable + purge + list; + if (modes == 0) + log_cmd_error("At least one of -enable, -disable, -purge or -list is required.\n"); + if (modes > 1) + log_cmd_error("Only one of -enable, -disable, -purge or -list may be present.\n"); + + if (all && !paths.empty()) + log_cmd_error("The -all option cannot be combined with a list of paths.\n"); + if (list && (all || !paths.empty())) + log_cmd_error("The -list mode takes no further options.\n"); + if (!list && !all && paths.empty()) + log("No paths specified, use -all to %s\n", purge ? "purge all paths" : "change the default setting"); + + if (list) { + log("Caching is %s by default.\n", LibertyAstCache::instance.cache_by_default ? "enabled" : "disabled"); + for (auto const &entry : LibertyAstCache::instance.cache_path) + log("Caching is %s for `%s'.\n", entry.second ? "enabled" : "disabled", entry.first.c_str()); + for (auto const &entry : LibertyAstCache::instance.cached) + log("Data for `%s' is currently cached.\n", entry.first.c_str()); + } else if (enable || disable) { + if (all) { + LibertyAstCache::instance.cache_by_default = enable; + } else { + for (auto const &path : paths) { + LibertyAstCache::instance.cache_path[path] = enable; + } + } + } else if (purge) { + if (all) { + LibertyAstCache::instance.cached.clear(); + LibertyAstCache::instance.cache_path.clear(); + } else { + for (auto const &path : paths) { + LibertyAstCache::instance.cached.erase(path); + LibertyAstCache::instance.cache_path.erase(path); + } + } + } else { + log_assert(false); + } + } +} LibcachePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index d3d5b7d57..d7a952603 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -32,6 +32,31 @@ using namespace Yosys; +#ifndef FILTERLIB + +LibertyAstCache LibertyAstCache::instance; + +std::shared_ptr LibertyAstCache::cached_ast(const std::string &fname) +{ + auto it = cached.find(fname); + if (it == cached.end()) + return nullptr; + log("Using cached data for liberty file `%s'\n", fname.c_str()); + return it->second; +} + +void LibertyAstCache::parsed_ast(const std::string &fname, const std::shared_ptr &ast) +{ + auto it = cache_path.find(fname); + bool should_cache = it == cache_path.end() ? cache_by_default : it->second; + if (!should_cache) + return; + log("Caching data for liberty file `%s'\n", fname.c_str()); + cached.emplace(fname, ast); +} + +#endif + bool LibertyInputStream::extend_buffer_once() { if (eof) diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 1fcaaebee..9332afebd 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -132,6 +132,22 @@ namespace Yosys } }; +#ifndef FILTERLIB + class LibertyAstCache { + LibertyAstCache() {}; + ~LibertyAstCache() {}; + public: + dict> cached; + + bool cache_by_default = false; + dict cache_path; + + std::shared_ptr cached_ast(const std::string &fname); + void parsed_ast(const std::string &fname, const std::shared_ptr &ast); + static LibertyAstCache instance; + }; +#endif + class LibertyMergedCells; class LibertyParser { @@ -152,15 +168,29 @@ namespace Yosys void error(const std::string &str) const; public: - const LibertyAst *ast; + std::shared_ptr shared_ast; + const LibertyAst *ast = nullptr; - LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {} - ~LibertyParser() { if (ast) delete ast; } + LibertyParser(std::istream &f) : f(f), line(1) { + shared_ast.reset(parse()); + ast = shared_ast.get(); + } + +#ifndef FILTERLIB + LibertyParser(std::istream &f, const std::string &fname) : f(f), line(1) { + shared_ast = LibertyAstCache::instance.cached_ast(fname); + if (!shared_ast) { + shared_ast.reset(parse()); + LibertyAstCache::instance.parsed_ast(fname, shared_ast); + } + ast = shared_ast.get(); + } +#endif }; class LibertyMergedCells { - std::vector asts; + std::vector> asts; public: std::vector cells; @@ -168,10 +198,7 @@ namespace Yosys { if (parser.ast) { const LibertyAst *ast = parser.ast; - asts.push_back(ast); - // The parser no longer owns its top level ast, but we do. - // sketchy zone - parser.ast = nullptr; + asts.push_back(parser.shared_ast); if (ast->id != "library") parser.error("Top level entity isn't \"library\".\n"); for (const LibertyAst *cell : ast->children) @@ -179,11 +206,6 @@ namespace Yosys cells.push_back(cell); } } - ~LibertyMergedCells() - { - for (auto ast : asts) - delete ast; - } }; } diff --git a/tests/liberty/libcache.ys b/tests/liberty/libcache.ys new file mode 100644 index 000000000..a741a9df1 --- /dev/null +++ b/tests/liberty/libcache.ys @@ -0,0 +1,58 @@ +libcache -enable busdef.lib + +logger -expect log "Caching is disabled by default." 1 +logger -expect log "Caching is enabled for `busdef.lib'." 1 +libcache -list +logger -check-expected + +logger -expect log "Caching data" 1 +log Caching data +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Caching data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +logger -expect log "Using caching data" 1 +log Using caching data +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +libcache -purge busdef.lib + +logger -expect log "Caching is disabled by default." 1 +logger -expect log "Caching is enabled for `busdef.lib'." 1 +log Caching is enabled for `busdef.lib'. +libcache -list +logger -check-expected + +libcache -enable -all + +logger -expect log "Caching is enabled by default." 1 +libcache -list +logger -check-expected + +logger -expect log "Caching data" 1 +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Caching data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +dfflibmap -liberty normal.lib +logger -check-expected