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
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<AstNode*>(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}; }
}

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)
: 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<char, WRITE_BUFFER_SIZE_BYTES>{}} {
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

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 {
// 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("<?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