diff --git a/Changes b/Changes index ad852bf44..a1151d3b5 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,8 @@ Verilator 4.211 devel **Minor:** * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] +* Output files are split based on the set of headers required + in order to aid incremental compilation via ccache (#3071). [Geza Lore] Verilator 4.210 2021-07-07 diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 78a9260c3..54c2a0566 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -59,8 +59,6 @@ class CUseVisitor final : public AstNVisitor { virtual void visit(AstClassRefDType* nodep) override { if (nodep->user1SetOnce()) return; // Process once if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); - // No class.h, it's inside the class package's h file - addNewUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name()); // Need to include extends() when we implement, but no need for pointers to know VL_RESTORER(m_impOnly); { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index cf947674c..eb1aa4de3 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -118,7 +118,6 @@ private: AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting int m_labelNum; // Next label number int m_splitSize; // # of cfunc nodes placed into output file - int m_splitFilenum; // File number being created, 0 = primary bool m_inUC = false; // Inside an AstUCStmt or AstUCMath std::vector m_blkChangeDetVec; // All encountered changes in block @@ -133,15 +132,11 @@ public: VL_DEBUG_FUNC; // Declare debug() // ACCESSORS - int splitFilenumInc() { - m_splitSize = 0; - return m_splitFilenum++; - } - int splitSize() const { return m_splitSize; } void splitSizeInc(int count) { m_splitSize += count; } void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); } + void splitSizeReset() { m_splitSize = 0; } bool splitNeeded() const { - return v3Global.opt.outputSplit() && splitSize() >= v3Global.opt.outputSplit(); + return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit(); } // METHODS @@ -1220,7 +1215,6 @@ public: m_wideTempRefp = nullptr; m_labelNum = 0; m_splitSize = 0; - m_splitFilenum = 0; } EmitCFunc(AstNode* nodep, V3OutCFile* ofp, bool trackText = false) : EmitCFunc{} { diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index dad347971..be6bca502 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -19,10 +19,126 @@ #include "V3Global.h" #include "V3EmitC.h" +#include "V3Ast.h" #include "V3EmitCFunc.h" +#include "V3String.h" +#include "V3UniqueNames.h" +#include +#include #include -#include + +//###################################################################### +// Visitor that gathers the headers required by an AstCFunc + +class EmitCGatherDependencies final : AstNVisitor { + // Ordered set, as it is used as a key in another map. + std::set m_dependencies; // Header names to be included in output C++ file + + // METHODS + void addSymsDependency() { m_dependencies.insert(EmitCBaseVisitor::symClassName()); } + void addModDependency(const AstNodeModule* modp) { + if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) { + m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(classp->classOrPackagep())); + } else { + m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(modp)); + } + } + void addDTypeDependency(const AstNodeDType* nodep) { + if (const AstClassRefDType* const dtypep = VN_CAST_CONST(nodep, ClassRefDType)) { + m_dependencies.insert( + EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep())); + } + } + void addSelfDependency(const string& selfPointer, AstNode* nodep) { + if (selfPointer.empty()) { + // No self pointer (e.g.: function locals, const pool values, loose static methods), + // so no dependency + } else if (VString::startsWith(selfPointer, "this")) { + // Dereferencing 'this', we need the definition of this module, which is also the + // module that contains the variable. + addModDependency(EmitCParentModule::get(nodep)); + } else { + // Must be an absolute reference + UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep, + "Unknown self pointer: '" << selfPointer << "'"); + // Dereferencing vlSymsp, so we need it's definition... + m_dependencies.insert(EmitCBaseVisitor::symClassName()); + } + } + + // VISITORS + virtual void visit(AstCCall* nodep) override { + addSelfDependency(nodep->selfPointer(), nodep->funcp()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCNew* nodep) override { + addDTypeDependency(nodep->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCMethodCall* nodep) override { + addDTypeDependency(nodep->fromp()->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstNewCopy* nodep) override { + addDTypeDependency(nodep->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstMemberSel* nodep) override { + addDTypeDependency(nodep->fromp()->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstNodeVarRef* nodep) override { + addSelfDependency(nodep->selfPointer(), nodep->varp()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCoverDecl* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstCoverInc* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstDumpCtl* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstScopeName* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstPrintTimeScale* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstTimeFormat* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstNodeSimpleText* nodep) override { + if (nodep->text().find("vlSymsp") != string::npos) { + m_dependencies.insert(EmitCBaseVisitor::symClassName()); + } + iterateChildrenConst(nodep); + } + virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + + // CONSTRUCTOR + explicit EmitCGatherDependencies(AstCFunc* cfuncp) { + // Strictly speaking, for loose methods, we could get away with just a forward + // declaration of the receiver class, but their body very likely includes at least one + // relative reference, so we are probably not loosing much. + addModDependency(EmitCParentModule::get(cfuncp)); + iterate(cfuncp); + } + +public: + static const std::set gather(AstCFunc* cfuncp) { + EmitCGatherDependencies visitor{cfuncp}; + return std::move(visitor.m_dependencies); + } +}; //###################################################################### // Internal EmitC implementation @@ -31,11 +147,15 @@ class EmitCImp final : EmitCFunc { // MEMBERS const AstNodeModule* const m_fileModp; // Files names/headers constructed using this module const bool m_slow; // Creating __Slow file + const std::set* m_requiredHeadersp; // Header files required by output file + std::string m_subFileName; // substring added to output filenames + V3UniqueNames m_uniqueNames; // For generating unique file names // METHODS - void openNextOutputFile() { + void openNextOutputFile(const std::set& headers, const string& subFileName) { UASSERT(!m_ofp, "Output file already open"); + splitSizeReset(); // Reset file size tracking m_lazyDecls.reset(); // Need to emit new lazy declarations if (v3Global.opt.lintOnly()) { @@ -46,7 +166,10 @@ class EmitCImp final : EmitCFunc { m_ofp = new V3OutCFile(filename); } else { string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); - if (const int filenum = splitFilenumInc()) filename += "__" + cvtToStr(filenum); + if (!subFileName.empty()) { + filename += "__" + subFileName; + filename = m_uniqueNames.get(filename); + } if (m_slow) filename += "__Slow"; filename += ".cpp"; newCFile(filename, /* slow: */ m_slow, /* source: */ true); @@ -58,21 +181,26 @@ class EmitCImp final : EmitCFunc { puts("// See " + topClassName() + ".h for the primary calling header\n"); // Include files + puts("\n#include \"verilated_heavy.h\"\n"); + if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n"); puts("\n"); - puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); - - if (v3Global.dpi()) { - puts("\n"); - puts("#include \"verilated_dpi.h\"\n"); - } - - emitModCUse(m_fileModp, VUseType::IMP_INCLUDE); - emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS); + for (const string& name : headers) puts("#include \"" + name + ".h\"\n"); emitTextSection(m_modp, AstType::atScImpHdr); } + void emitStaticVarDefns(const AstNodeModule* modp) { + // Emit static variable definitions + const string modName = prefixNameProtect(modp); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isStatic()) { + puts(varp->vlArgType(true, false, false, modName)); + puts(";\n"); + } + } + } + } void emitParamDefns(const AstNodeModule* modp) { for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { @@ -168,8 +296,9 @@ class EmitCImp final : EmitCFunc { void emitCoverageImp() { if (v3Global.opt.coverage()) { puts("\n// Coverage\n"); - // Rather than putting out VL_COVER_INSERT calls directly, we do it via this function - // This gets around gcc slowness constructing all of the template arguments. + // Rather than putting out VL_COVER_INSERT calls directly, we do it via this + // function. This gets around gcc slowness constructing all of the template + // arguments. puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); @@ -289,19 +418,23 @@ class EmitCImp final : EmitCFunc { } } } - void emitAll(const AstNodeModule* modp) { + // Predicate to check if we actually need to emit anything into the common implementation file. + // Used to avoid creating empty output files. + bool hasCommonImp(const AstNodeModule* modp) const { + // Nothing to emit if no module! + if (!modp) return false; + // We always need the slow file + if (m_slow) return true; + // The fast file is only required when we have ScImp nodes + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, ScImp)) return true; + } + return false; + } + // Actually emit common implementation contents for given AstNodeModule + void doCommonImp(const AstNodeModule* modp) { if (m_slow) { - // Emit static variable definitions - const string modName = prefixNameProtect(modp); - for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isStatic()) { - puts(varp->vlArgType(true, false, false, modName)); - puts(";\n"); - } - } - } - + emitStaticVarDefns(modp); if (!VN_IS(modp, Class)) { emitParamDefns(modp); emitCtorImp(modp); @@ -311,30 +444,86 @@ class EmitCImp final : EmitCFunc { emitSavableImp(modp); emitCoverageImp(); } else { + // From `systemc_implementation emitTextSection(modp, AstType::atScImp); } + } + void emitCommonImp(const AstNodeModule* modp) { + const AstClass* const classp + = VN_IS(modp, ClassPackage) ? VN_CAST_CONST(modp, ClassPackage)->classp() : nullptr; - // Emit all AstCFunc - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) iterate(funcp); + if (hasCommonImp(modp) || hasCommonImp(classp)) { + std::set headers; + headers.insert(prefixNameProtect(m_fileModp)); + headers.insert(symClassName()); + + openNextOutputFile(headers, ""); + + doCommonImp(modp); + if (classp) { + VL_RESTORER(m_modp); + m_modp = classp; + doCommonImp(classp); + } + + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + } + void emitCFuncImp(const AstNodeModule* modp) { + // Partition functions based on which module definitions they require, by building a + // map from "AstNodeModules whose definitions are required" -> "functions that need + // them" + std::map, std::vector> depSet2funcps; + + const auto gather = [this, &depSet2funcps](const AstNodeModule* modp) { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { + // TRACE_* and DPI handled elsewhere + if (funcp->isTrace()) continue; + if (funcp->dpiImportPrototype()) continue; + if (funcp->dpiExportDispatcher()) continue; + if (funcp->slow() != m_slow) continue; + const auto& depSet = EmitCGatherDependencies::gather(funcp); + depSet2funcps[depSet].push_back(funcp); + } + } + }; + + gather(modp); + if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { + gather(packagep->classp()); + } + + // Emit all functions in each dependency set into separate files + for (const auto& pair : depSet2funcps) { + m_requiredHeadersp = &pair.first; + // Compute the hash of the dependencies, so we can add it to the filenames to + // disambiguate them + V3Hash hash; + for (const string& name : *m_requiredHeadersp) { hash += name; } + m_subFileName = "DepSet_" + cvtToHex(hash.value()); + // Open output file + openNextOutputFile(*m_requiredHeadersp, m_subFileName); + // Emit functions in this dependency set + for (AstCFunc* const funcp : pair.second) { + VL_RESTORER(m_modp); + m_modp = EmitCParentModule::get(funcp); + iterate(funcp); + } + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); } } // VISITORS virtual void visit(AstCFunc* nodep) override { - // TRACE_* and DPI handled elsewhere - if (nodep->isTrace()) return; - if (nodep->dpiImportPrototype()) return; - if (nodep->dpiExportDispatcher()) return; - if (nodep->slow() != m_slow) return; - if (splitNeeded()) { // Splitting file, so using parallel build. v3Global.useParallelBuild(true); // Close old file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); // Open a new file - openNextOutputFile(); + openNextOutputFile(*m_requiredHeadersp, m_subFileName); } EmitCFunc::visit(nodep); @@ -347,20 +536,16 @@ class EmitCImp final : EmitCFunc { m_modp = modp; - openNextOutputFile(); + // Emit implementation of this module, if this is an AstClassPackage, then put the + // corresponding AstClass implementation in the same file as often optimziations are + // possible when both are seen by the compiler + // TODO: is the above comment still true? - emitAll(modp); + // Emit implementations of common parts + emitCommonImp(modp); - if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { - // Put the non-static class implementation in same C++ files as - // often optimizations are possible when both are seen by the - // compiler together - m_modp = packagep->classp(); - emitAll(packagep->classp()); - } - - // Close output file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Emit implementations of all AstCFunc + emitCFuncImp(modp); } virtual ~EmitCImp() override = default; @@ -380,21 +565,24 @@ class EmitCTrace final : EmitCFunc { // MEMBERS const bool m_slow; // Making slow file int m_enumNum = 0; // Enumeration number (whole netlist) + V3UniqueNames m_uniqueNames; // For generating unique file names // METHODS - void newOutCFile(int filenum) { + void openNextOutputFile() { + UASSERT(!m_ofp, "Output file already open"); + + splitSizeReset(); // Reset file size tracking m_lazyDecls.reset(); // Need to emit new lazy declarations string filename = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace")); - if (filenum) filename += "__" + cvtToStr(filenum); - filename += (m_slow ? "__Slow" : ""); + filename = m_uniqueNames.get(filename); + if (m_slow) filename += "__Slow"; filename += ".cpp"; AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/); cfilep->support(true); - if (m_ofp) v3fatalSrc("Previous file not closed"); if (optSystemC()) { m_ofp = new V3OutScFile(filename); } else { @@ -647,7 +835,7 @@ class EmitCTrace final : EmitCFunc { // Close old file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); // Open a new file - newOutCFile(splitFilenumInc()); + openNextOutputFile(); } EmitCFunc::visit(nodep); @@ -678,7 +866,7 @@ class EmitCTrace final : EmitCFunc { : m_slow{slow} { m_modp = modp; // Open output file - newOutCFile(splitFilenumInc()); + openNextOutputFile(); // Emit functions for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 2e47ff004..d0c6ccb83 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -20,11 +20,13 @@ #include "V3Global.h" #include "V3EmitC.h" #include "V3EmitCFunc.h" +#include "V3UniqueNames.h" #include #include class EmitCModel final : public EmitCFunc { + V3UniqueNames m_uniqueNames; // For generating unique file names // METHODS VL_DEBUG_FUNC; @@ -584,11 +586,13 @@ class EmitCModel final : public EmitCFunc { } if (!m_ofp) { - const string filename = v3Global.opt.makeDir() + "/" + topClassName() - + "__Dpi_Export_" + cvtToStr(splitFilenumInc()) + ".cpp"; + string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi_Export"; + filename = m_uniqueNames.get(filename); + filename += ".cpp"; newCFile(filename, /* slow: */ false, /* source: */ true); m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; + splitSizeReset(); // Reset file size tracking m_lazyDecls.reset(); m_ofp->putsHeader(); puts( diff --git a/src/V3UniqueNames.h b/src/V3UniqueNames.h new file mode 100644 index 000000000..2d3f94560 --- /dev/null +++ b/src/V3UniqueNames.h @@ -0,0 +1,39 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Basic data structure to keep names unique +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2005-2021 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +// +//************************************************************************* + +#ifndef VERILATOR_V3UNIQUENAMES_H_ +#define VERILATOR_V3UNIQUENAMES_H_ +#include "config_build.h" +#include "verilatedos.h" + +#include +#include + +class V3UniqueNames final { + std::unordered_map m_multiplicity; // Suffix number for given key + +public: + // Return argument, appended with a unique suffix each time we are called with the same + // argument. + std::string get(const std::string& name) { + const unsigned num = m_multiplicity.emplace(name, 0).first->second++; + return name + "__" + cvtToStr(num); + } +}; + +#endif // Guard diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 9c1a56408..bf6efd9bc 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -2371,6 +2371,33 @@ sub tries { return 2; } +sub glob_all { + my $self = (ref $_[0]? shift : $Self); + my $pattern = shift; + + return glob($pattern); +} + +sub glob_one { + my $self = (ref $_[0]? shift : $Self); + my $pattern = shift; + return if $self->errors || $self->skips || $self->unsupporteds; + + my @files = glob($pattern); + my $n = scalar @files; + if ($n == 0) { + $self->error("glob_one: pattern '$pattern' does not match any files\n"); + } elsif ($n != 1) { + my $msg = "glob_one: pattern '$pattern' matches multiple files:\n"; + foreach my $file (@files) { + $msg .= $file."\n"; + } + $self->error($msg); + } else { + return $files[0]; + } +} + sub file_grep_not { my $self = (ref $_[0]? shift : $Self); my $filename = shift; @@ -2402,6 +2429,30 @@ sub file_grep { } } +sub file_grep_any { + my $self = $Self; + my @filenames = @{$_[0]}; shift; + my $regexp = shift; + my $expvalue = shift; + return if $self->errors || $self->skips || $self->unsupporteds; + + foreach my $filename (@filenames) { + my $contents = $self->file_contents($filename); + return if ($contents eq "_Already_Errored_"); + if ($contents =~ /$regexp/) { + if ($expvalue && $expvalue ne $1) { + $self->error("file_grep: $filename: Got='$1' Expected='$expvalue' in regexp: $regexp\n"); + } + return; + } + } + my $msg = "file_grep_any: Regexp '$regexp' not found in any of the following files:\n"; + foreach my $filename (@filenames) { + $msg .= $filename."\n"; + } + $self->error($msg); +} + my %_File_Contents_Cache; sub file_contents { diff --git a/test_regress/t/t_alw_noreorder.pl b/test_regress/t/t_alw_noreorder.pl index b0543df38..7d6eceb88 100755 --- a/test_regress/t/t_alw_noreorder.pl +++ b/test_regress/t/t_alw_noreorder.pl @@ -19,8 +19,9 @@ file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); # Here we should see some dly vars since reorder is disabled. # (Whereas our twin test, t_alw_reorder, should see no dly vars # since it enables the reorder step.) -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/dly__t__DOT__v1/i); -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/dly__t__DOT__v2/i); +my @files = glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp"); +file_grep_any(\@files, qr/dly__t__DOT__v1/i); +file_grep_any(\@files, qr/dly__t__DOT__v2/i); execute( check_finished=>1, diff --git a/test_regress/t/t_alw_reorder.pl b/test_regress/t/t_alw_reorder.pl index 1335722ca..dce2f101f 100755 --- a/test_regress/t/t_alw_reorder.pl +++ b/test_regress/t/t_alw_reorder.pl @@ -18,8 +18,10 @@ file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); # Important: if reorder succeeded, we should see no dly vars. # Equally important: twin test t_alw_noreorder should see dly vars, # is identical to this test except for disabling the reorder step. -foreach my $file ("$Self->{obj_dir}/$Self->{VM_PREFIX}.cpp", - "$Self->{obj_dir}/$Self->{VM_PREFIX}.h") { +foreach my $file ( + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.h"), + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp") + ) { file_grep_not($file, qr/dly__t__DOT__v1/i); file_grep_not($file, qr/dly__t__DOT__v2/i); file_grep_not($file, qr/dly__t__DOT__v3/i); diff --git a/test_regress/t/t_c_this.pl b/test_regress/t/t_c_this.pl index 37616efd7..b8edcffb4 100755 --- a/test_regress/t/t_c_this.pl +++ b/test_regress/t/t_c_this.pl @@ -15,7 +15,7 @@ compile(); if ($Self->{vlt_all}) { # The word 'this' (but only the whole word 'this' should have been replaced # in the contents. - my $file = "$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp"; + my $file = glob_one("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__DepSet_*__0.cpp"); my $text = file_contents($file); error("$file has 'this->clk'") if ($text =~ m/\bthis->clk\b/); error("$file does not have 'xthis'") if ($text !~ m/\bxthis\b/); diff --git a/test_regress/t/t_flag_comp_limit_parens.pl b/test_regress/t/t_flag_comp_limit_parens.pl index 11731a873..51fb7f932 100755 --- a/test_regress/t/t_flag_comp_limit_parens.pl +++ b/test_regress/t/t_flag_comp_limit_parens.pl @@ -18,7 +18,7 @@ execute( check_finished => 1, ); -file_grep("$Self->{obj_dir}/Vt_flag_comp_limit_parens___024root__Slow.cpp", qr/Vdeeptemp/x); +file_grep(glob_one("$Self->{obj_dir}/Vt_flag_comp_limit_parens___024root__DepSet_*__0__Slow.cpp"), qr/Vdeeptemp/x); ok(1); 1; diff --git a/test_regress/t/t_flag_csplit_off.pl b/test_regress/t/t_flag_csplit_off.pl index d29b8f2ef..8e3014653 100755 --- a/test_regress/t/t_flag_csplit_off.pl +++ b/test_regress/t/t_flag_csplit_off.pl @@ -61,7 +61,7 @@ while (1) { sub check_no_splits { foreach my $file (glob("$Self->{obj_dir}/*.cpp")) { $file =~ s/__024root//; - if ($file =~ qr/__\d/) { + if ($file =~ qr/__[1-9]/) { error("Split file found: $file"); } } diff --git a/test_regress/t/t_flag_xinitial_unique.pl b/test_regress/t/t_flag_xinitial_unique.pl index 572880d86..f2451bd3f 100755 --- a/test_regress/t/t_flag_xinitial_unique.pl +++ b/test_regress/t/t_flag_xinitial_unique.pl @@ -18,7 +18,7 @@ execute( check_finished => 1, ); -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/VL_RAND_RESET/); +file_grep(glob_one("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__DepSet_*__0__Slow.cpp"), qr/VL_RAND_RESET/); ok(1); 1; diff --git a/test_regress/t/t_foreach.pl b/test_regress/t/t_foreach.pl index 06fc908f0..5c5b93dd0 100755 --- a/test_regress/t/t_foreach.pl +++ b/test_regress/t/t_foreach.pl @@ -20,14 +20,16 @@ execute( # We expect all loops should be unrolled by verilator, # none of the loop variables should exist in the output: -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/index_/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/index_/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp")) { + file_grep_not($file, qr/index_/); +} # Further, we expect that all logic within the loop should # have been evaluated inside the compiler. So there should be # no references to 'sum' in the .cpp. -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/sum/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/sum/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp")) { + file_grep_not($file, qr/sum/); +} ok(1); 1; diff --git a/test_regress/t/t_inst_tree_inl0_pub1.pl b/test_regress/t/t_inst_tree_inl0_pub1.pl index 3bf53b1f1..effe5ebbe 100755 --- a/test_regress/t/t_inst_tree_inl0_pub1.pl +++ b/test_regress/t/t_inst_tree_inl0_pub1.pl @@ -23,17 +23,18 @@ sub checkRelativeRefs { my ($mod, $expect_relative) = @_; my $found_relative = 0; - my $file = "$Self->{obj_dir}/V$Self->{name}_${mod}.cpp"; - my $text = file_contents($file); + foreach my $file (glob_all("$Self->{obj_dir}/V$Self->{name}_${mod}*.cpp")) { + my $text = file_contents($file); - if ($text =~ m/this->/ || $text =~ m/vlSelf->/) { - $found_relative = 1; - } + if ($text =~ m/this->/ || $text =~ m/vlSelf->/) { + $found_relative = 1; + } - if ($found_relative != $expect_relative) { - error("$file " . - ($found_relative ? "has" : "does not have") . - " relative variable references."); + if ($found_relative != $expect_relative) { + error("$file " . + ($found_relative ? "has" : "does not have") . + " relative variable references."); + } } } diff --git a/test_regress/t/t_optm_if_array.pl b/test_regress/t/t_optm_if_array.pl index 2b2ca158f..aa9716f06 100755 --- a/test_regress/t/t_optm_if_array.pl +++ b/test_regress/t/t_optm_if_array.pl @@ -17,8 +17,9 @@ execute( check_finished => 1, ); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/rstn_r/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/rstn_r/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp")) { + file_grep_not($file, qr/rstn_r/); +} ok(1); 1; diff --git a/test_regress/t/t_optm_redor.pl b/test_regress/t/t_optm_redor.pl index 2b2ca158f..aa9716f06 100755 --- a/test_regress/t/t_optm_redor.pl +++ b/test_regress/t/t_optm_redor.pl @@ -17,8 +17,9 @@ execute( check_finished => 1, ); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/rstn_r/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/rstn_r/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp")) { + file_grep_not($file, qr/rstn_r/); +} ok(1); 1; diff --git a/test_regress/t/t_trace_ena_cc.pl b/test_regress/t/t_trace_ena_cc.pl index ff3debaf3..ea30e0880 100755 --- a/test_regress/t/t_trace_ena_cc.pl +++ b/test_regress/t/t_trace_ena_cc.pl @@ -21,8 +21,8 @@ execute( ); if ($Self->{vlt_all}) { - file_grep ("$Self->{obj_dir}/V$Self->{name}__Trace__Slow.cpp", qr/c_trace_on\"/x); - file_grep_not ("$Self->{obj_dir}/V$Self->{name}__Trace__Slow.cpp", qr/_trace_off\"/x); + file_grep ("$Self->{obj_dir}/V$Self->{name}__Trace__0__Slow.cpp", qr/c_trace_on\"/x); + file_grep_not ("$Self->{obj_dir}/V$Self->{name}__Trace__0__Slow.cpp", qr/_trace_off\"/x); file_grep ("$Self->{obj_dir}/simx.vcd", qr/\$enddefinitions/x); file_grep_not ("$Self->{obj_dir}/simx.vcd", qr/inside_sub/x);