From 119e998f120832ca7e5d61f434cc5efd37fa5197 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 1 Apr 2025 13:48:44 +0200 Subject: [PATCH] read_liberty: Faster input handling for the liberty lexer The lexer for liberty files was using istream's `get` and `unget` which are notorious for bad performance and that showed up during profiling. This replaces the direct `istream` use with a custom LibertyInputStream that does its own buffering to provide `get` and `unget` that behave the same way but are implemented with a fast path that is easy to inline and optimize. --- kernel/yosys_common.h | 6 +++++ passes/techmap/libparse.cc | 45 ++++++++++++++++++++++++++++++++++++++ passes/techmap/libparse.h | 33 +++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index a68539ce1..6fadf788f 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -128,6 +128,12 @@ # error "C++17 or later compatible compiler is required" #endif +#if defined(__has_cpp_attribute) && __has_cpp_attribute(gnu::cold) +# define YS_COLD [[gnu::cold]] +#else +# define YS_COLD +#endif + #include "kernel/io.h" YOSYS_NAMESPACE_BEGIN diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 06dd6288e..dbf191080 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -32,6 +32,51 @@ using namespace Yosys; +bool LibertyInputStream::extend_buffer_once() +{ + if (eof) + return false; + + // To support unget we leave the last already read character in the buffer + if (buf_pos > 1) { + size_t move_pos = buf_pos - 1; + memmove(buffer.data(), buffer.data() + move_pos, buf_end - move_pos); + buf_pos -= move_pos; + buf_end -= move_pos; + } + + const size_t chunk_size = 4096; + if (buffer.size() < buf_end + chunk_size) { + buffer.resize(buf_end + chunk_size); + } + + size_t read_size = f.rdbuf()->sgetn(buffer.data() + buf_end, chunk_size); + buf_end += read_size; + if (read_size < chunk_size) + eof = true; + return read_size != 0; +} + +bool LibertyInputStream::extend_buffer_at_least(size_t size) { + while (buffered_size() < size) { + if (!extend_buffer_once()) + return false; + } + return true; +} + +int LibertyInputStream::get_cold() +{ + if (buf_pos == buf_end) { + if (!extend_buffer_at_least()) + return EOF; + } + + int c = buffer[buf_pos]; + buf_pos += 1; + return c; +} + LibertyAst::~LibertyAst() { for (auto child : children) diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 16808fc58..eb73e296d 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -90,12 +90,43 @@ namespace Yosys bool eval(dict& values); }; + class LibertyInputStream { + std::istream &f; + std::vector buffer; + size_t buf_pos = 0; + size_t buf_end = 0; + bool eof = false; + + bool extend_buffer_once(); + bool extend_buffer_at_least(size_t size = 1); + + YS_COLD int get_cold(); + + public: + LibertyInputStream(std::istream &f) : f(f) {} + + size_t buffered_size() { return buf_end - buf_pos; } + const char *buffered_data() { return buffer.data() + buf_pos; } + + int get() { + if (buf_pos == buf_end) + return get_cold(); + int c = buffer[buf_pos]; + buf_pos += 1; + return c; + } + + void unget() { + buf_pos -= 1; + } + }; + class LibertyMergedCells; class LibertyParser { friend class LibertyMergedCells; private: - std::istream &f; + LibertyInputStream f; int line; /* lexer return values: