From 9d2adf3e49f9e6174fd31b9863c406d3d3d2af4f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 30 Jul 2025 12:41:21 +0100 Subject: [PATCH] Internals: Enable EmitV to output formatted code to std::ostream (#6239) Introduce V3OutStream as a V3OutFormatter that writes to a stream instead of a file. This can be used to emit formatted code fragments e.g. in debug prints and graph dumps. --- src/V3EmitV.cpp | 63 +++++++++++++++++++++++++++---------------------- src/V3File.cpp | 17 +++++++++---- src/V3File.h | 29 +++++++++++++++++++---- 3 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index be6addce6..e9121517b 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -28,12 +28,13 @@ VL_DEFINE_DEBUG_FUNCTIONS; // ###################################################################### // Emit statements and expressions -class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { +class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { // STATE - across all visitors - const bool m_suppressUnknown = false; + const bool m_alwaysTrackText; // Always track all NodeSimpleText + const bool m_suppressUnknown; // Do not error on unknown node // STATE - for current visit position (use VL_RESTORER) - AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE + AstSenTree* m_sensesp = nullptr; // Domain for printing one a ALWAYS under a ACTIVE bool m_suppressSemi = false; // Non-statement, don't print ; bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars bool m_arrayPost = false; // Print array information that goes after identifier (vs after) @@ -72,7 +73,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { // VISITORS void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); } void visit(AstNodeModule* nodep) override { - putfs(nodep, nodep->verilogKwd() + " " + prefixNameProtect(nodep) + ";\n"); + putfs(nodep, nodep->verilogKwd() + " " + EmitCBase::prefixNameProtect(nodep) + ";\n"); iterateChildrenConst(nodep); putqs(nodep, "end" + nodep->verilogKwd() + "\n"); } @@ -461,7 +462,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { puts(";\n"); } void visit(AstNodeSimpleText* nodep) override { - if (nodep->tracking() || m_trackText) { + if (nodep->tracking() || m_alwaysTrackText) { puts(nodep->text()); } else { putsNoTracking(nodep->text()); @@ -965,9 +966,9 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } public: - explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp) - : m_suppressUnknown{suppressUnknown} - , m_sensesp{domainp} {} + explicit EmitVBaseVisitorConst(bool alwaysTrackText, bool suppressUnknown) + : m_alwaysTrackText{alwaysTrackText} + , m_suppressUnknown{suppressUnknown} {} ~EmitVBaseVisitorConst() override = default; }; @@ -975,18 +976,19 @@ public: // Emit to an output file class EmitVFileVisitor final : public EmitVBaseVisitorConst { + // STATE + V3OutVFile& m_of; // The output file // METHODS - void puts(const string& str) override { ofp()->puts(str); } - void putbs(const string& str) override { ofp()->putbs(str); } + void putsNoTracking(const string& str) override { m_of.putsNoTracking(str); } + void puts(const string& str) override { m_of.puts(str); } + void putbs(const string& str) override { m_of.putbs(str); } void putfs(AstNode*, const string& str) override { putbs(str); } void putqs(AstNode*, const string& str) override { putbs(str); } - void putsNoTracking(const string& str) override { ofp()->putsNoTracking(str); } public: - EmitVFileVisitor(AstNode* nodep, V3OutVFile* ofp, bool trackText, bool suppressUnknown) - : EmitVBaseVisitorConst{suppressUnknown, nullptr} { - m_ofp = ofp; - m_trackText = trackText; + EmitVFileVisitor(AstNode* nodep, V3OutVFile& of, bool alwaysTrackText, bool suppressUnknown) + : EmitVBaseVisitorConst{alwaysTrackText, suppressUnknown} + , m_of{of} { iterateConst(nodep); } ~EmitVFileVisitor() override = default; @@ -996,19 +998,25 @@ public: // Emit to a stream (perhaps stringstream) class EmitVStreamVisitor final : public EmitVBaseVisitorConst { - // MEMBERS - std::ostream& m_os; + // STATE + V3OutStream m_os; // The output stream formatter + bool m_tracking; // Use line tracking // METHODS - void putsNoTracking(const string& str) override { m_os << str; } - void puts(const string& str) override { putsNoTracking(str); } - void putbs(const string& str) override { puts(str); } + void putsNoTracking(const string& str) override { m_os.putsNoTracking(str); } + void puts(const string& str) override { + m_tracking ? m_os.puts(str) : m_os.putsNoTracking(str); + } + void putbs(const string& str) override { + m_tracking ? m_os.putbs(str) : m_os.putsNoTracking(str); + } void putfs(AstNode*, const string& str) override { putbs(str); } void putqs(AstNode*, const string& str) override { putbs(str); } public: - EmitVStreamVisitor(const AstNode* nodep, std::ostream& os) - : EmitVBaseVisitorConst{false, nullptr} - , m_os(os) { // Need () or GCC 4.8 false warning + EmitVStreamVisitor(const AstNode* nodep, std::ostream& os, bool tracking) + : EmitVBaseVisitorConst{false, false} + , m_os{os, V3OutFormatter::LA_VERILOG} + , m_tracking{tracking} { iterateConst(const_cast(nodep)); } ~EmitVStreamVisitor() override = default; @@ -1018,7 +1026,7 @@ public: // EmitV class functions void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) { - { EmitVStreamVisitor{nodep, os}; } + { EmitVStreamVisitor{nodep, os, /* tracking: */ false}; } } void V3EmitV::emitvFiles() { @@ -1028,9 +1036,8 @@ void V3EmitV::emitvFiles() { AstVFile* const vfilep = VN_CAST(filep, VFile); if (vfilep && vfilep->tblockp()) { V3OutVFile of{vfilep->name()}; - of.puts("// DESCR" - "IPTION: Verilator generated Verilog\n"); - { EmitVFileVisitor{vfilep->tblockp(), &of, true, false}; } + of.puts("// DESCRIPTION: Verilator generated Verilog\n"); + { EmitVFileVisitor{vfilep->tblockp(), of, true, false}; } } } } @@ -1038,5 +1045,5 @@ void V3EmitV::emitvFiles() { void V3EmitV::debugEmitV(const string& filename) { UINFO(2, __FUNCTION__ << ":"); V3OutVFile of{filename}; - { EmitVFileVisitor{v3Global.rootp(), &of, true, true}; } + { EmitVFileVisitor{v3Global.rootp(), of, true, true}; } } diff --git a/src/V3File.cpp b/src/V3File.cpp index 10b3232d6..d2c7cf973 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -640,11 +640,10 @@ bool VInFilter::readWholefile(const string& filename, VInFilter::StrList& outl) } //###################################################################### -// V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code. +// V3OutFormatter: A class for printing code with automatic indentation. -V3OutFormatter::V3OutFormatter(const string& filename, V3OutFormatter::Language lang) - : m_filename{filename} - , m_lang{lang} { +V3OutFormatter::V3OutFormatter(V3OutFormatter::Language lang) + : m_lang{lang} { m_blockIndent = v3Global.opt.decoration() ? 4 : 1; m_commaWidth = v3Global.opt.decoration() ? 50 : 150; } @@ -962,7 +961,8 @@ void V3OutFormatter::printf(const char* fmt...) { // V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code. V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang) - : V3OutFormatter{filename, lang} + : V3OutFormatter{lang} + , m_filename{filename} , m_bufferp{new std::array{}} { if ((m_fp = V3File::new_fopen_w(filename)) == nullptr) { v3fatal("Can't write file: " << filename); @@ -992,6 +992,13 @@ void V3OutCFile::putsGuard() { puts("#define " + var + " // guard\n"); } +//###################################################################### +// V3OutStream + +V3OutStream::V3OutStream(std::ostream& ostream, V3OutFormatter::Language lang) + : V3OutFormatter{lang} + , m_ostream{ostream} {} + //###################################################################### // VIdProtect diff --git a/src/V3File.h b/src/V3File.h index 5c75ed1a5..009b658e7 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -102,7 +102,7 @@ public: }; //============================================================================ -// V3OutFormatter: A class for automatic indentation of C++ or Verilog code. +// V3OutFormatter: A class for automatic indentation of output code. class V3OutFormatter VL_NOT_FINAL { // TYPES @@ -113,7 +113,6 @@ public: private: // MEMBERS - const string m_filename; const Language m_lang; // Indenting Verilog code int m_blockIndent; // Characters per block indent int m_commaWidth; // Width after which to break at ,'s @@ -132,10 +131,9 @@ private: void putcNoTracking(char chr); public: - V3OutFormatter(const string& filename, Language lang); + V3OutFormatter(Language lang); virtual ~V3OutFormatter() = default; // ACCESSORS - string filename() const { return m_filename; } int column() const { return m_column; } int blockIndent() const { return m_blockIndent; } void blockIndent(int flag) { m_blockIndent = flag; } @@ -165,7 +163,7 @@ public: void indentInc() { m_indentLevel += m_blockIndent; } void indentDec() { m_indentLevel -= m_blockIndent; - UASSERT(m_indentLevel >= 0, ": " << m_filename << ": Underflow of indentation"); + UASSERT(m_indentLevel >= 0, "Underflow of indentation"); } void blockInc() { m_parenVec.push(m_indentLevel + m_blockIndent); } void blockDec() { @@ -201,6 +199,7 @@ class V3OutFile VL_NOT_FINAL : public V3OutFormatter { static constexpr std::size_t WRITE_BUFFER_SIZE_BYTES = 128 * 1024; // MEMBERS + const std::string m_filename; FILE* m_fp = nullptr; std::size_t m_usedBytes = 0; // Number of bytes stored in m_bufferp std::size_t m_writtenBytes = 0; // Number of bytes written to output @@ -214,6 +213,8 @@ public: V3OutFile& operator=(V3OutFile&&) = delete; ~V3OutFile() override; + std::string filename() const { return m_filename; } + void putsForceIncs(); void statRecordWritten() { @@ -430,6 +431,24 @@ public: virtual void putsHeader() { puts("\n"); } }; +//============================================================================ +// V3OutStream: A class for printing formatted code to any std::ostream + +class V3OutStream VL_NOT_FINAL : public V3OutFormatter { + // MEMBERS + std::ostream& m_ostream; + + VL_UNCOPYABLE(V3OutStream); + VL_UNMOVABLE(V3OutStream); + +public: + V3OutStream(std::ostream& ostream, V3OutFormatter::Language lang); + ~V3OutStream() override = default; + + void putcOutput(char chr) override { m_ostream << chr; }; + void putsOutput(const char* str) override { m_ostream << str; }; +}; + //============================================================================ // VIdProtect: Hash identifier names in output files to protect them