Workaround GCC/clang bug with huge compile times, bug1248.

This commit is contained in:
Wilson Snyder 2017-12-09 11:52:35 -05:00
parent 1b605ade94
commit 345657ab32
6 changed files with 189 additions and 54 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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