Emit implementations into separate files based on required headers.

This patch partitions AstCFuncs under an AstNodeModule based on which
header files they require for their implementation, and emits them
into separate files based on the distinct dependency sets. This helps
with incremental recompilation of the output C++.
This commit is contained in:
Geza Lore 2021-07-14 22:37:37 +01:00
parent 8bb77f86ec
commit ab4063f098
18 changed files with 378 additions and 94 deletions

View File

@ -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

View File

@ -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);
{

View File

@ -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<AstChangeDet*> 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{} {

View File

@ -19,10 +19,126 @@
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3Ast.h"
#include "V3EmitCFunc.h"
#include "V3String.h"
#include "V3UniqueNames.h"
#include <map>
#include <set>
#include <vector>
#include <unordered_set>
//######################################################################
// 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<string> 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<std::string> 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<string>* 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<string>& 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>" : "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<string> 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<const std::set<string>, std::vector<AstCFunc*>> 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); }

View File

@ -20,11 +20,13 @@
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCFunc.h"
#include "V3UniqueNames.h"
#include <algorithm>
#include <vector>
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(

39
src/V3UniqueNames.h Normal file
View File

@ -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 <string>
#include <unordered_map>
class V3UniqueNames final {
std::unordered_map<std::string, unsigned> 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

View File

@ -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 {

View File

@ -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,

View File

@ -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);

View File

@ -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/);

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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.");
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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);