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.
This commit is contained in:
Geza Lore 2025-07-30 12:41:21 +01:00 committed by GitHub
parent 2b62f5a625
commit 9d2adf3e49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 71 additions and 38 deletions

View File

@ -28,12 +28,13 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// ###################################################################### // ######################################################################
// Emit statements and expressions // Emit statements and expressions
class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
// STATE - across all visitors // 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) // 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_suppressSemi = false; // Non-statement, don't print ;
bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars
bool m_arrayPost = false; // Print array information that goes after identifier (vs after) 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 // VISITORS
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); } void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
void visit(AstNodeModule* nodep) override { void visit(AstNodeModule* nodep) override {
putfs(nodep, nodep->verilogKwd() + " " + prefixNameProtect(nodep) + ";\n"); putfs(nodep, nodep->verilogKwd() + " " + EmitCBase::prefixNameProtect(nodep) + ";\n");
iterateChildrenConst(nodep); iterateChildrenConst(nodep);
putqs(nodep, "end" + nodep->verilogKwd() + "\n"); putqs(nodep, "end" + nodep->verilogKwd() + "\n");
} }
@ -461,7 +462,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
puts(";\n"); puts(";\n");
} }
void visit(AstNodeSimpleText* nodep) override { void visit(AstNodeSimpleText* nodep) override {
if (nodep->tracking() || m_trackText) { if (nodep->tracking() || m_alwaysTrackText) {
puts(nodep->text()); puts(nodep->text());
} else { } else {
putsNoTracking(nodep->text()); putsNoTracking(nodep->text());
@ -965,9 +966,9 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
} }
public: public:
explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp) explicit EmitVBaseVisitorConst(bool alwaysTrackText, bool suppressUnknown)
: m_suppressUnknown{suppressUnknown} : m_alwaysTrackText{alwaysTrackText}
, m_sensesp{domainp} {} , m_suppressUnknown{suppressUnknown} {}
~EmitVBaseVisitorConst() override = default; ~EmitVBaseVisitorConst() override = default;
}; };
@ -975,18 +976,19 @@ public:
// Emit to an output file // Emit to an output file
class EmitVFileVisitor final : public EmitVBaseVisitorConst { class EmitVFileVisitor final : public EmitVBaseVisitorConst {
// STATE
V3OutVFile& m_of; // The output file
// METHODS // METHODS
void puts(const string& str) override { ofp()->puts(str); } void putsNoTracking(const string& str) override { m_of.putsNoTracking(str); }
void putbs(const string& str) override { ofp()->putbs(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 putfs(AstNode*, const string& str) override { putbs(str); }
void putqs(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: public:
EmitVFileVisitor(AstNode* nodep, V3OutVFile* ofp, bool trackText, bool suppressUnknown) EmitVFileVisitor(AstNode* nodep, V3OutVFile& of, bool alwaysTrackText, bool suppressUnknown)
: EmitVBaseVisitorConst{suppressUnknown, nullptr} { : EmitVBaseVisitorConst{alwaysTrackText, suppressUnknown}
m_ofp = ofp; , m_of{of} {
m_trackText = trackText;
iterateConst(nodep); iterateConst(nodep);
} }
~EmitVFileVisitor() override = default; ~EmitVFileVisitor() override = default;
@ -996,19 +998,25 @@ public:
// Emit to a stream (perhaps stringstream) // Emit to a stream (perhaps stringstream)
class EmitVStreamVisitor final : public EmitVBaseVisitorConst { class EmitVStreamVisitor final : public EmitVBaseVisitorConst {
// MEMBERS // STATE
std::ostream& m_os; V3OutStream m_os; // The output stream formatter
bool m_tracking; // Use line tracking
// METHODS // METHODS
void putsNoTracking(const string& str) override { m_os << str; } void putsNoTracking(const string& str) override { m_os.putsNoTracking(str); }
void puts(const string& str) override { putsNoTracking(str); } void puts(const string& str) override {
void putbs(const string& str) override { puts(str); } 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 putfs(AstNode*, const string& str) override { putbs(str); }
void putqs(AstNode*, const string& str) override { putbs(str); } void putqs(AstNode*, const string& str) override { putbs(str); }
public: public:
EmitVStreamVisitor(const AstNode* nodep, std::ostream& os) EmitVStreamVisitor(const AstNode* nodep, std::ostream& os, bool tracking)
: EmitVBaseVisitorConst{false, nullptr} : EmitVBaseVisitorConst{false, false}
, m_os(os) { // Need () or GCC 4.8 false warning , m_os{os, V3OutFormatter::LA_VERILOG}
, m_tracking{tracking} {
iterateConst(const_cast<AstNode*>(nodep)); iterateConst(const_cast<AstNode*>(nodep));
} }
~EmitVStreamVisitor() override = default; ~EmitVStreamVisitor() override = default;
@ -1018,7 +1026,7 @@ public:
// EmitV class functions // EmitV class functions
void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) { void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) {
{ EmitVStreamVisitor{nodep, os}; } { EmitVStreamVisitor{nodep, os, /* tracking: */ false}; }
} }
void V3EmitV::emitvFiles() { void V3EmitV::emitvFiles() {
@ -1028,9 +1036,8 @@ void V3EmitV::emitvFiles() {
AstVFile* const vfilep = VN_CAST(filep, VFile); AstVFile* const vfilep = VN_CAST(filep, VFile);
if (vfilep && vfilep->tblockp()) { if (vfilep && vfilep->tblockp()) {
V3OutVFile of{vfilep->name()}; V3OutVFile of{vfilep->name()};
of.puts("// DESCR" of.puts("// DESCRIPTION: Verilator generated Verilog\n");
"IPTION: Verilator generated Verilog\n"); { EmitVFileVisitor{vfilep->tblockp(), of, true, false}; }
{ EmitVFileVisitor{vfilep->tblockp(), &of, true, false}; }
} }
} }
} }
@ -1038,5 +1045,5 @@ void V3EmitV::emitvFiles() {
void V3EmitV::debugEmitV(const string& filename) { void V3EmitV::debugEmitV(const string& filename) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
V3OutVFile of{filename}; V3OutVFile of{filename};
{ EmitVFileVisitor{v3Global.rootp(), &of, true, true}; } { EmitVFileVisitor{v3Global.rootp(), of, true, true}; }
} }

View File

@ -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) V3OutFormatter::V3OutFormatter(V3OutFormatter::Language lang)
: m_filename{filename} : m_lang{lang} {
, m_lang{lang} {
m_blockIndent = v3Global.opt.decoration() ? 4 : 1; m_blockIndent = v3Global.opt.decoration() ? 4 : 1;
m_commaWidth = v3Global.opt.decoration() ? 50 : 150; 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. // V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code.
V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang) V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang)
: V3OutFormatter{filename, lang} : V3OutFormatter{lang}
, m_filename{filename}
, m_bufferp{new std::array<char, WRITE_BUFFER_SIZE_BYTES>{}} { , m_bufferp{new std::array<char, WRITE_BUFFER_SIZE_BYTES>{}} {
if ((m_fp = V3File::new_fopen_w(filename)) == nullptr) { if ((m_fp = V3File::new_fopen_w(filename)) == nullptr) {
v3fatal("Can't write file: " << filename); v3fatal("Can't write file: " << filename);
@ -992,6 +992,13 @@ void V3OutCFile::putsGuard() {
puts("#define " + var + " // guard\n"); puts("#define " + var + " // guard\n");
} }
//######################################################################
// V3OutStream
V3OutStream::V3OutStream(std::ostream& ostream, V3OutFormatter::Language lang)
: V3OutFormatter{lang}
, m_ostream{ostream} {}
//###################################################################### //######################################################################
// VIdProtect // VIdProtect

View File

@ -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 { class V3OutFormatter VL_NOT_FINAL {
// TYPES // TYPES
@ -113,7 +113,6 @@ public:
private: private:
// MEMBERS // MEMBERS
const string m_filename;
const Language m_lang; // Indenting Verilog code const Language m_lang; // Indenting Verilog code
int m_blockIndent; // Characters per block indent int m_blockIndent; // Characters per block indent
int m_commaWidth; // Width after which to break at ,'s int m_commaWidth; // Width after which to break at ,'s
@ -132,10 +131,9 @@ private:
void putcNoTracking(char chr); void putcNoTracking(char chr);
public: public:
V3OutFormatter(const string& filename, Language lang); V3OutFormatter(Language lang);
virtual ~V3OutFormatter() = default; virtual ~V3OutFormatter() = default;
// ACCESSORS // ACCESSORS
string filename() const { return m_filename; }
int column() const { return m_column; } int column() const { return m_column; }
int blockIndent() const { return m_blockIndent; } int blockIndent() const { return m_blockIndent; }
void blockIndent(int flag) { m_blockIndent = flag; } void blockIndent(int flag) { m_blockIndent = flag; }
@ -165,7 +163,7 @@ public:
void indentInc() { m_indentLevel += m_blockIndent; } void indentInc() { m_indentLevel += m_blockIndent; }
void indentDec() { void indentDec() {
m_indentLevel -= m_blockIndent; 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 blockInc() { m_parenVec.push(m_indentLevel + m_blockIndent); }
void blockDec() { void blockDec() {
@ -201,6 +199,7 @@ class V3OutFile VL_NOT_FINAL : public V3OutFormatter {
static constexpr std::size_t WRITE_BUFFER_SIZE_BYTES = 128 * 1024; static constexpr std::size_t WRITE_BUFFER_SIZE_BYTES = 128 * 1024;
// MEMBERS // MEMBERS
const std::string m_filename;
FILE* m_fp = nullptr; FILE* m_fp = nullptr;
std::size_t m_usedBytes = 0; // Number of bytes stored in m_bufferp 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 std::size_t m_writtenBytes = 0; // Number of bytes written to output
@ -214,6 +213,8 @@ public:
V3OutFile& operator=(V3OutFile&&) = delete; V3OutFile& operator=(V3OutFile&&) = delete;
~V3OutFile() override; ~V3OutFile() override;
std::string filename() const { return m_filename; }
void putsForceIncs(); void putsForceIncs();
void statRecordWritten() { void statRecordWritten() {
@ -430,6 +431,24 @@ public:
virtual void putsHeader() { puts("<?xml version=\"1.0\" ?>\n"); } virtual void putsHeader() { puts("<?xml version=\"1.0\" ?>\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 // VIdProtect: Hash identifier names in output files to protect them