forked from github/verilator
Workaround GCC/clang bug with huge compile times, bug1248.
This commit is contained in:
parent
1b605ade94
commit
345657ab32
2
Changes
2
Changes
@ -4,6 +4,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
* Verilator 3.917 devel
|
||||
|
||||
*** Workaround GCC/clang bug with huge compile times, bug1248.
|
||||
|
||||
**** Support > 64 bit decimal $display.
|
||||
|
||||
**** Support string len() method. [Victor Besyakov]
|
||||
|
149
src/V3EmitC.cpp
149
src/V3EmitC.cpp
@ -75,7 +75,8 @@ public:
|
||||
const string& vfmt, char fmtLetter);
|
||||
|
||||
void emitVarDecl(AstVar* nodep, const string& prefixIfImp);
|
||||
typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_PAR, EVL_ALL} EisWhich;
|
||||
typedef enum {EVL_CLASS_IO, EVL_CLASS_SIG, EVL_CLASS_TEMP, EVL_CLASS_PAR, EVL_CLASS_ALL,
|
||||
EVL_FUNC_ALL} EisWhich;
|
||||
void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp);
|
||||
void emitVarCtors();
|
||||
bool emitSimpleOk(AstNodeMath* nodep);
|
||||
@ -892,8 +893,8 @@ class EmitCImp : EmitCStmts {
|
||||
if (varp->isFuncReturn()) emitVarDecl(varp, "");
|
||||
}
|
||||
}
|
||||
emitVarList(nodep->initsp(), EVL_ALL, "");
|
||||
emitVarList(nodep->stmtsp(), EVL_ALL, "");
|
||||
emitVarList(nodep->initsp(), EVL_FUNC_ALL, "");
|
||||
emitVarList(nodep->stmtsp(), EVL_FUNC_ALL, "");
|
||||
|
||||
nodep->initsp()->iterateAndNext(*this);
|
||||
|
||||
@ -1652,7 +1653,7 @@ void EmitCImp::emitSavableImp(AstNodeModule* modp) {
|
||||
void EmitCImp::emitStaticDecl(AstNodeModule* modp) {
|
||||
// Need implementation here. Be careful of alignment code; needs to be uniquified
|
||||
// with module name to avoid multiple symbols.
|
||||
//emitVarList(modp->stmtsp(), EVL_ALL, modp->name());
|
||||
//emitVarList(modp->stmtsp(), EVL_FUNC_ALL, modp->name());
|
||||
puts(""); // NOP for cppcheck, otherwise const function
|
||||
}
|
||||
|
||||
@ -1799,41 +1800,101 @@ void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& pref
|
||||
// This aids cache packing and locality
|
||||
// Largest->smallest reduces the number of pad variables.
|
||||
// But for now, Smallest->largest makes it more likely a small offset will allow access to the signal.
|
||||
// TODO: Move this sort to an earlier visitor stage.
|
||||
typedef std::multimap<int, AstVar*> VarSortMap;
|
||||
VarSortMap varAnonMap;
|
||||
VarSortMap varNonanonMap;
|
||||
int anonMembers = 0;
|
||||
|
||||
for (int isstatic=1; isstatic>=0; isstatic--) {
|
||||
if (prefixIfImp!="" && !isstatic) continue;
|
||||
const int sortmax = 9;
|
||||
for (int sort=0; sort<sortmax; sort++) {
|
||||
if (sort==3) continue;
|
||||
for (AstNode* nodep=firstp; nodep; nodep = nodep->nextp()) {
|
||||
if (AstVar* varp = nodep->castVar()) {
|
||||
bool doit = true;
|
||||
switch (which) {
|
||||
case EVL_ALL: doit = true; break;
|
||||
case EVL_IO: doit = varp->isIO(); break;
|
||||
case EVL_SIG: doit = (varp->isSignal() && !varp->isIO()); break;
|
||||
case EVL_TEMP: doit = (varp->isTemp() && !varp->isIO()); break;
|
||||
case EVL_PAR: doit = (varp->isParam() && !varp->valuep()->castConst()); break;
|
||||
default: v3fatalSrc("Bad Case");
|
||||
}
|
||||
if (varp->isStatic() ? !isstatic : isstatic) doit=false;
|
||||
if (doit) {
|
||||
int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes();
|
||||
int sortbytes = sortmax-1;
|
||||
if (varp->isUsedClock() && varp->widthMin()==1) sortbytes = 0;
|
||||
else if (varp->dtypeSkipRefp()->castUnpackArrayDType()) sortbytes=8;
|
||||
else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes=7;
|
||||
else if (varp->isScBv() || varp->isScBigUint()) sortbytes=6;
|
||||
else if (sigbytes==8) sortbytes=5;
|
||||
else if (sigbytes==4) sortbytes=4;
|
||||
else if (sigbytes==2) sortbytes=2;
|
||||
else if (sigbytes==1) sortbytes=1;
|
||||
if (sort==sortbytes) {
|
||||
emitVarDecl(varp, prefixIfImp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prefixIfImp!="" && !isstatic) continue;
|
||||
for (AstNode* nodep=firstp; nodep; nodep = nodep->nextp()) {
|
||||
if (AstVar* varp = nodep->castVar()) {
|
||||
bool doit = true;
|
||||
switch (which) {
|
||||
case EVL_CLASS_IO: doit = varp->isIO(); break;
|
||||
case EVL_CLASS_SIG: doit = (varp->isSignal() && !varp->isIO()); break;
|
||||
case EVL_CLASS_TEMP: doit = (varp->isTemp() && !varp->isIO()); break;
|
||||
case EVL_CLASS_PAR: doit = (varp->isParam() && !varp->valuep()->castConst()); break;
|
||||
case EVL_CLASS_ALL: doit = true; break;
|
||||
case EVL_FUNC_ALL: doit = true; break;
|
||||
default: v3fatalSrc("Bad Case");
|
||||
}
|
||||
if (varp->isStatic() ? !isstatic : isstatic) doit=false;
|
||||
if (doit) {
|
||||
int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes();
|
||||
int sortbytes = 9;
|
||||
if (varp->isUsedClock() && varp->widthMin()==1) sortbytes = 0;
|
||||
else if (varp->dtypeSkipRefp()->castUnpackArrayDType()) sortbytes=8;
|
||||
else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes=7;
|
||||
else if (varp->isScBv() || varp->isScBigUint()) sortbytes=6;
|
||||
else if (sigbytes==8) sortbytes=5;
|
||||
else if (sigbytes==4) sortbytes=4;
|
||||
else if (sigbytes==2) sortbytes=2;
|
||||
else if (sigbytes==1) sortbytes=1;
|
||||
|
||||
bool anonOk = (v3Global.opt.compLimitMembers() != 0 // Enabled
|
||||
&& !varp->isStatic()
|
||||
&& !varp->isIO() // Confusing to user
|
||||
&& !varp->isSc() // Aggregates can't be anon
|
||||
&& (varp->basicp() && !varp->basicp()->isOpaque()) // Aggregates can't be anon
|
||||
&& which != EVL_FUNC_ALL); // Anon not legal in funcs, and gcc bug free there anyhow
|
||||
if (anonOk) {
|
||||
++anonMembers;
|
||||
varAnonMap.insert(make_pair(sortbytes, varp));
|
||||
} else {
|
||||
varNonanonMap.insert(make_pair(sortbytes, varp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output anons
|
||||
{
|
||||
int lim = v3Global.opt.compLimitMembers();
|
||||
int anonL3s = 1;
|
||||
int anonL2s = 1;
|
||||
int anonL1s = 1;
|
||||
if (anonMembers > (lim*lim*lim)) {
|
||||
anonL3s = (anonMembers + (lim*lim*lim) - 1) / (lim*lim*lim);
|
||||
anonL2s = lim;
|
||||
anonL1s = lim;
|
||||
} else if (anonMembers > (lim*lim)) {
|
||||
anonL2s = (anonMembers + (lim*lim) - 1) / (lim*lim);
|
||||
anonL1s = lim;
|
||||
} else if (anonMembers > lim) {
|
||||
anonL1s = (anonMembers + lim - 1) / lim;
|
||||
}
|
||||
if (anonL1s != 1) puts("// Anonymous structures to workaround compiler member-count bugs\n");
|
||||
VarSortMap::iterator it = varAnonMap.begin();
|
||||
for (int l3=0; l3<anonL3s && it != varAnonMap.end(); ++l3) {
|
||||
if (anonL3s != 1) puts("struct {\n");
|
||||
for (int l2=0; l2<anonL2s && it != varAnonMap.end(); ++l2) {
|
||||
if (anonL2s != 1) puts("struct {\n");
|
||||
for (int l1=0; l1<anonL1s && it != varAnonMap.end(); ++l1) {
|
||||
if (anonL1s != 1) puts("struct {\n");
|
||||
for (int l0=0; l0<lim && it != varAnonMap.end(); ++l0) {
|
||||
AstVar* varp = it->second;
|
||||
emitVarDecl(varp, prefixIfImp);
|
||||
++it;
|
||||
}
|
||||
if (anonL1s != 1) puts("};\n");
|
||||
}
|
||||
if (anonL2s != 1) puts("};\n");
|
||||
}
|
||||
if (anonL3s != 1) puts("};\n");
|
||||
}
|
||||
// Leftovers, just in case off by one error somewhere above
|
||||
for (; it != varAnonMap.end(); ++it) {
|
||||
AstVar* varp = it->second;
|
||||
emitVarDecl(varp, prefixIfImp);
|
||||
}
|
||||
}
|
||||
// Output nonanons
|
||||
for (VarSortMap::iterator it = varNonanonMap.begin(); it != varNonanonMap.end(); ++it) {
|
||||
AstVar* varp = it->second;
|
||||
emitVarDecl(varp, prefixIfImp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1945,15 +2006,15 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
puts("\n// PORTS\n");
|
||||
if (modp->isTop()) puts("// The application code writes and reads these signals to\n");
|
||||
if (modp->isTop()) puts("// propagate new values into/out from the Verilated model.\n");
|
||||
emitVarList(modp->stmtsp(), EVL_IO, "");
|
||||
emitVarList(modp->stmtsp(), EVL_CLASS_IO, "");
|
||||
|
||||
puts("\n// LOCAL SIGNALS\n");
|
||||
if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
|
||||
emitVarList(modp->stmtsp(), EVL_SIG, "");
|
||||
emitVarList(modp->stmtsp(), EVL_CLASS_SIG, "");
|
||||
|
||||
puts("\n// LOCAL VARIABLES\n");
|
||||
if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
|
||||
emitVarList(modp->stmtsp(), EVL_TEMP, "");
|
||||
emitVarList(modp->stmtsp(), EVL_CLASS_TEMP, "");
|
||||
|
||||
puts("\n// INTERNAL VARIABLES\n");
|
||||
if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
|
||||
@ -1970,7 +2031,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
puts("\n// PARAMETERS\n");
|
||||
if (modp->isTop()) puts("// Parameters marked /*verilator public*/ for use by application code\n");
|
||||
ofp()->putsPrivate(false); // public:
|
||||
emitVarList(modp->stmtsp(), EVL_PAR, ""); // Only those that are non-CONST
|
||||
emitVarList(modp->stmtsp(), EVL_CLASS_PAR, ""); // Only those that are non-CONST
|
||||
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstVar* varp = nodep->castVar()) {
|
||||
if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) {
|
||||
@ -2100,7 +2161,7 @@ void EmitCImp::emitImp(AstNodeModule* modp) {
|
||||
if (m_slow && splitFilenum()==0) {
|
||||
puts("\n//--------------------\n");
|
||||
puts("// STATIC VARIABLES\n\n");
|
||||
emitVarList(modp->stmtsp(), EVL_ALL, modClassName(modp));
|
||||
emitVarList(modp->stmtsp(), EVL_CLASS_ALL, modClassName(modp));
|
||||
}
|
||||
|
||||
if (m_fast && splitFilenum()==0) {
|
||||
@ -2410,7 +2471,7 @@ class EmitCTrace : EmitCStmts {
|
||||
} else nodep->v3fatalSrc("Bad Case");
|
||||
|
||||
if (nodep->initsp()) putsDecoration("// Variables\n");
|
||||
emitVarList(nodep->initsp(), EVL_ALL, "");
|
||||
emitVarList(nodep->initsp(), EVL_FUNC_ALL, "");
|
||||
nodep->initsp()->iterateAndNext(*this);
|
||||
|
||||
putsDecoration("// Body\n");
|
||||
|
@ -732,7 +732,19 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
shift;
|
||||
addCFlags(argv[i]);
|
||||
}
|
||||
else if ( !strcmp (sw, "-comp-limits-syms") && (i+1)<argc ) { // Undocumented
|
||||
else if ( !strcmp (sw, "-comp-limit-blocks") && (i+1)<argc ) { // Undocumented
|
||||
shift;
|
||||
m_compLimitBlocks = atoi(argv[i]);
|
||||
}
|
||||
else if ( !strcmp (sw, "-comp-limit-members") && (i+1)<argc ) { // Undocumented
|
||||
shift;
|
||||
m_compLimitMembers = atoi(argv[i]);
|
||||
}
|
||||
else if ( !strcmp (sw, "-comp-limit-parens") && (i+1)<argc ) { // Undocumented
|
||||
shift;
|
||||
m_compLimitParens = atoi(argv[i]);
|
||||
}
|
||||
else if ( !strcmp (sw, "-comp-limit-syms") && (i+1)<argc ) { // Undocumented
|
||||
shift;
|
||||
VName::maxLength(atoi(argv[i]));
|
||||
}
|
||||
@ -945,13 +957,17 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if ( !strcmp (sw, "-compiler") && (i+1)<argc) {
|
||||
shift;
|
||||
if (!strcmp (argv[i], "clang")) {
|
||||
m_compLimitParens = 80; // limit unknown
|
||||
m_compLimitBlocks = 80; // limit unknown
|
||||
m_compLimitBlocks = 80; // limit unknown
|
||||
m_compLimitMembers = 50; // soft limit, has slowdown bug as of clang++ 3.8
|
||||
m_compLimitParens = 80; // limit unknown
|
||||
} else if (!strcmp (argv[i], "gcc")) {
|
||||
m_compLimitParens = 0;
|
||||
m_compLimitBlocks = 0; // Bug free
|
||||
m_compLimitMembers = 50; // soft limit, has slowdown bug as of g++ 7.1
|
||||
m_compLimitParens = 0; // Bug free
|
||||
} else if (!strcmp (argv[i], "msvc")) {
|
||||
m_compLimitParens = 80; // 128, but allow some room
|
||||
m_compLimitBlocks = 80; // 128, but allow some room
|
||||
m_compLimitBlocks = 80; // 128, but allow some room
|
||||
m_compLimitMembers = 0; // probably ok, and AFAIK doesn't support anon structs
|
||||
m_compLimitParens = 80; // 128, but allow some room
|
||||
} else {
|
||||
fl->v3fatal("Unknown setting for --compiler: "<<argv[i]);
|
||||
}
|
||||
@ -1248,8 +1264,9 @@ V3Options::V3Options() {
|
||||
m_unrollCount = 64;
|
||||
m_unrollStmts = 30000;
|
||||
|
||||
m_compLimitParens = 0;
|
||||
m_compLimitBlocks = 0;
|
||||
m_compLimitMembers = 50;
|
||||
m_compLimitParens = 0;
|
||||
|
||||
m_makeDir = "obj_dir";
|
||||
m_bin = "";
|
||||
|
@ -121,8 +121,9 @@ class V3Options {
|
||||
int m_unrollCount; // main switch: --unroll-count
|
||||
int m_unrollStmts; // main switch: --unroll-stmts
|
||||
|
||||
int m_compLimitBlocks; // compiler selection options
|
||||
int m_compLimitParens; // compiler selection options
|
||||
int m_compLimitBlocks; // compiler selection; number of nested blocks
|
||||
int m_compLimitMembers; // compiler selection; number of members in struct before make anon array
|
||||
int m_compLimitParens; // compiler selection; number of nested parens
|
||||
|
||||
string m_bin; // main switch: --bin {binary}
|
||||
string m_exeName; // main switch: -o {name}
|
||||
@ -268,6 +269,7 @@ class V3Options {
|
||||
int unrollStmts() const { return m_unrollStmts; }
|
||||
|
||||
int compLimitBlocks() const { return m_compLimitBlocks; }
|
||||
int compLimitMembers() const { return m_compLimitMembers; }
|
||||
int compLimitParens() const { return m_compLimitParens; }
|
||||
|
||||
string exeName() const { return m_exeName!="" ? m_exeName : prefix(); }
|
||||
|
@ -392,7 +392,8 @@ sub new {
|
||||
verilator_flags => ["-cc",
|
||||
"-Mdir $self->{obj_dir}",
|
||||
"-OD", # As currently disabled unless -O3
|
||||
"--debug-check"],
|
||||
"--debug-check",
|
||||
"--comp-limit-members 10", ],
|
||||
verilator_flags2 => [],
|
||||
verilator_flags3 => ["--clk clk"],
|
||||
verilator_make_gcc => 1,
|
||||
|
52
test_regress/t/t_emit_memb_limit.pl
Executable file
52
test_regress/t/t_emit_memb_limit.pl
Executable file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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.
|
||||
use IO::File;
|
||||
|
||||
sub gen {
|
||||
my $filename = shift;
|
||||
my $n = shift;
|
||||
|
||||
my $fh = IO::File->new(">$filename");
|
||||
$fh->print("// Generated by t_emit_memb_limit.pl\n");
|
||||
$fh->print("module t (i,clk,o);\n");
|
||||
$fh->print(" input clk;\n");
|
||||
$fh->print(" input i;\n");
|
||||
$fh->print(" output o;\n");
|
||||
for (my $i=0; $i<($n+1); ++$i) {
|
||||
$fh->print(" logic r$i;\n");
|
||||
}
|
||||
$fh->print(" always @ (posedge clk) begin\n");
|
||||
$fh->print(" r0 <= i;\n");
|
||||
for (my $i=1; $i<$n; ++$i) {
|
||||
$fh->print(" r".($i+1)." <= r$i;\n");
|
||||
}
|
||||
$fh->print(" o <= r$n;\n");
|
||||
$fh->print(' $write("*-* All Finished *-*\n");',"\n");
|
||||
$fh->print(' $finish;',"\n");
|
||||
$fh->print(" end\n");
|
||||
$fh->print("endmodule\n");
|
||||
}
|
||||
|
||||
top_filename("$Self->{obj_dir}/t_emit_memb_limit.v");
|
||||
|
||||
# Current limit is 50, so want to test at least 50*50 cases
|
||||
gen($Self->{top_filename}, 6000);
|
||||
|
||||
compile (
|
||||
verilator_flags2=>["-x-assign fast --x-initial fast"],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
file_grep ("$Self->{obj_dir}/$Self->{VM_PREFIX}.h", qr/struct \{/);
|
||||
|
||||
ok(1);
|
||||
1;
|
Loading…
Reference in New Issue
Block a user