From 345657ab325413091eeaa7e1fafa6db08ff6c512 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 9 Dec 2017 11:52:35 -0500 Subject: [PATCH] Workaround GCC/clang bug with huge compile times, bug1248. --- Changes | 2 + src/V3EmitC.cpp | 149 ++++++++++++++++++++-------- src/V3Options.cpp | 31 ++++-- src/V3Options.h | 6 +- test_regress/driver.pl | 3 +- test_regress/t/t_emit_memb_limit.pl | 52 ++++++++++ 6 files changed, 189 insertions(+), 54 deletions(-) create mode 100755 test_regress/t/t_emit_memb_limit.pl diff --git a/Changes b/Changes index 78fdb4a77..4f981c9f2 100644 --- a/Changes +++ b/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] diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 4b66679cd..c5874cbc3 100644 --- a/src/V3EmitC.cpp +++ b/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 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; sortnextp()) { - 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; l3second; + 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"); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index e2c16303c..ad5fb7cf8 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -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)v3fatal("Unknown setting for --compiler: "< ["-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, diff --git a/test_regress/t/t_emit_memb_limit.pl b/test_regress/t/t_emit_memb_limit.pl new file mode 100755 index 000000000..54f65baa6 --- /dev/null +++ b/test_regress/t/t_emit_memb_limit.pl @@ -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;