1 // -*- mode: C++; c-file-style: "cc-mode" -*-
2 //*************************************************************************
3 // DESCRIPTION: Verilator: Options parsing
4 //
5 // Code available from: https://verilator.org
6 //
7 //*************************************************************************
8 //
9 // Copyright 2003-2021 by Wilson Snyder. This program is free software; you
10 // can redistribute it and/or modify it under the terms of either the GNU
11 // Lesser General Public License Version 3 or the Perl Artistic License
12 // Version 2.0.
13 // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
14 //
15 //*************************************************************************
16 
17 #include "config_build.h"
18 #include "verilatedos.h"
19 
20 #include "V3Global.h"
21 #include "V3Ast.h"
22 #include "V3Os.h"
23 #include "V3Options.h"
24 #include "V3OptionParser.h"
25 #include "V3Error.h"
26 #include "V3File.h"
27 #include "V3PreShell.h"
28 #include "V3String.h"
29 
30 // clang-format off
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #ifndef _WIN32
34 # include <sys/utsname.h>
35 #endif
36 #include <algorithm>
37 #include <cctype>
38 #include <dirent.h>
39 #include <fcntl.h>
40 #include <list>
41 #include <map>
42 #include <memory>
43 #include <set>
44 
45 #include "config_rev.h"
46 
47 #if defined(_WIN32) || defined(__MINGW32__)
48 # include <io.h>  // open, close
49 #endif
50 // clang-format on
51 
52 //######################################################################
53 // V3 Internal state
54 
55 class V3OptionsImp final {
56 public:
57     // TYPES
58     using DirMap = std::map<const string, std::set<std::string>>;  // Directory listing
59 
60     // STATE
61     std::list<string> m_allArgs;  // List of every argument encountered
62     std::list<string> m_incDirUsers;  // Include directories (ordered)
63     std::set<string> m_incDirUserSet;  // Include directories (for removing duplicates)
64     std::list<string> m_incDirFallbacks;  // Include directories (ordered)
65     std::set<string> m_incDirFallbackSet;  // Include directories (for removing duplicates)
66     std::map<const string, V3LangCode> m_langExts;  // Language extension map
67     std::list<string> m_libExtVs;  // Library extensions (ordered)
68     std::set<string> m_libExtVSet;  // Library extensions (for removing duplicates)
69     DirMap m_dirMap;  // Directory listing
70 
71     // ACCESSOR METHODS
addIncDirUser(const string & incdir)72     void addIncDirUser(const string& incdir) {
73         if (m_incDirUserSet.find(incdir) == m_incDirUserSet.end()) {
74             // cppcheck-suppress stlFindInsert  // cppcheck 1.90 bug
75             m_incDirUserSet.insert(incdir);
76             m_incDirUsers.push_back(incdir);
77             m_incDirFallbacks.remove(incdir);  // User has priority over Fallback
78             m_incDirFallbackSet.erase(incdir);  // User has priority over Fallback
79         }
80     }
addIncDirFallback(const string & incdir)81     void addIncDirFallback(const string& incdir) {
82         if (m_incDirUserSet.find(incdir)
83             == m_incDirUserSet.end()) {  // User has priority over Fallback
84             if (m_incDirFallbackSet.find(incdir) == m_incDirFallbackSet.end()) {
85                 // cppcheck-suppress stlFindInsert  // cppcheck 1.90 bug
86                 m_incDirFallbackSet.insert(incdir);
87                 m_incDirFallbacks.push_back(incdir);
88             }
89         }
90     }
addLangExt(const string & langext,const V3LangCode & lc)91     void addLangExt(const string& langext, const V3LangCode& lc) {
92         // New language extension replaces any pre-existing one.
93         (void)m_langExts.erase(langext);
94         m_langExts[langext] = lc;
95     }
96 
addLibExtV(const string & libext)97     void addLibExtV(const string& libext) {
98         if (m_libExtVSet.find(libext) == m_libExtVSet.end()) {
99             // cppcheck-suppress stlFindInsert  // cppcheck 1.90 bug
100             m_libExtVSet.insert(libext);
101             m_libExtVs.push_back(libext);
102         }
103     }
104     V3OptionsImp() = default;
105     ~V3OptionsImp() = default;
106 };
107 
108 //######################################################################
109 // V3LangCode class functions
110 
V3LangCode(const char * textp)111 V3LangCode::V3LangCode(const char* textp) {
112     // Return code for given string, or ERROR, which is a bad code
113     for (int codei = V3LangCode::L_ERROR; codei < V3LangCode::_ENUM_END; ++codei) {
114         const V3LangCode code = V3LangCode(codei);
115         if (0 == VL_STRCASECMP(textp, code.ascii())) {
116             m_e = code;
117             return;
118         }
119     }
120     m_e = V3LangCode::L_ERROR;
121 }
122 
123 //######################################################################
124 // VTimescale class functions
125 
VTimescale(const string & value,bool & badr)126 VTimescale::VTimescale(const string& value, bool& badr)
127     : m_e{VTimescale::NONE} {
128     badr = true;
129     const string spaceless = VString::removeWhitespace(value);
130     for (int i = TS_100S; i < _ENUM_END; ++i) {
131         const VTimescale ts(i);
132         if (spaceless == ts.ascii()) {
133             badr = false;
134             m_e = ts.m_e;
135             break;
136         }
137     }
138 }
139 
140 //######################################################################
141 // V3HierarchicalBlockOption class functions
142 
143 // Parse "--hierarchical-block orig_name,mangled_name,param0_name,param0_value,... " option.
144 // The format of value is as same as -G option. (can be string literal surrounded by ")
V3HierarchicalBlockOption(const string & opts)145 V3HierarchicalBlockOption::V3HierarchicalBlockOption(const string& opts) {
146     V3StringList vals;
147     bool inStr = false;
148     string cur;
149     static const string hierBlock("--hierarchical-block");
150     FileLine cmdfl(FileLine::commandLineFilename());
151     // Split by ','. If ',' appears between "", that is not a separator.
152     for (string::const_iterator it = opts.begin(); it != opts.end();) {
153         if (inStr) {
154             if (*it == '\\') {
155                 ++it;
156                 if (it == opts.end()) {
157                     cmdfl.v3error(hierBlock + " must not end with \\");
158                     break;
159                 }
160                 if (*it != '"' && *it != '\\') {
161                     cmdfl.v3error(hierBlock + " does not allow '" + *it + "' after \\");
162                     break;
163                 }
164                 cur.push_back(*it);
165                 ++it;
166             } else if (*it == '"') {  // end of string
167                 cur.push_back(*it);
168                 vals.push_back(cur);
169                 cur.clear();
170                 ++it;
171                 if (it != opts.end()) {
172                     if (*it != ',') {
173                         cmdfl.v3error(hierBlock + " expects ',', but '" + *it + "' is passed");
174                         break;
175                     }
176                     ++it;
177                     if (it == opts.end()) {
178                         cmdfl.v3error(hierBlock + " must not end with ','");
179                         break;
180                     }
181                     inStr = *it == '"';
182                     cur.push_back(*it);
183                     ++it;
184                 }
185             } else {
186                 cur.push_back(*it);
187                 ++it;
188             }
189         } else {
190             if (*it == '"') {
191                 cmdfl.v3error(hierBlock + " does not allow '\"' in the middle of literal");
192                 break;
193             }
194             if (*it == ',') {  // end of this parameter
195                 vals.push_back(cur);
196                 cur.clear();
197                 ++it;
198                 if (it == opts.end()) {
199                     cmdfl.v3error(hierBlock + " must not end with ','");
200                     break;
201                 }
202                 inStr = *it == '"';
203             }
204             cur.push_back(*it);
205             ++it;
206         }
207     }
208     if (!cur.empty()) vals.push_back(cur);
209     if (vals.size() >= 2) {
210         if (vals.size() % 2) {
211             cmdfl.v3error(hierBlock + " requires the number of entries to be even");
212         }
213         m_origName = vals[0];
214         m_mangledName = vals[1];
215     } else {
216         cmdfl.v3error(hierBlock + " requires at least two comma-separated values");
217     }
218     for (size_t i = 2; i + 1 < vals.size(); i += 2) {
219         const bool inserted = m_parameters.insert(std::make_pair(vals[i], vals[i + 1])).second;
220         if (!inserted) {
221             cmdfl.v3error("Module name '" + vals[i] + "' is duplicated in " + hierBlock);
222         }
223     }
224 }
225 
226 //######################################################################
227 // V3Options class functions
228 
parseSlashed(FileLine * fl,const char * textp,VTimescale & unitr,VTimescale & precr,bool allowEmpty)229 void VTimescale::parseSlashed(FileLine* fl, const char* textp, VTimescale& unitr,
230                               VTimescale& precr, bool allowEmpty) {
231     // Parse `timescale of <number><units> / <number><units>
232     unitr = VTimescale::NONE;
233     precr = VTimescale::NONE;
234 
235     const char* cp = textp;
236     for (; isspace(*cp); ++cp) {}
237     const char* const unitp = cp;
238     for (; *cp && *cp != '/'; ++cp) {}
239     const string unitStr(unitp, cp - unitp);
240     for (; isspace(*cp); ++cp) {}
241     string precStr;
242     if (*cp == '/') {
243         ++cp;
244         for (; isspace(*cp); ++cp) {}
245         const char* const precp = cp;
246         for (; *cp && *cp != '/'; ++cp) {}
247         precStr = string(precp, cp - precp);
248     }
249     for (; isspace(*cp); ++cp) {}
250     if (*cp) {
251         fl->v3error("`timescale syntax error: '" << textp << "'");
252         return;
253     }
254 
255     bool unitbad;
256     const VTimescale unit(unitStr, unitbad /*ref*/);
257     if (unitbad && !(unitStr.empty() && allowEmpty)) {
258         fl->v3error("`timescale timeunit syntax error: '" << unitStr << "'");
259         return;
260     }
261     unitr = unit;
262 
263     if (!precStr.empty()) {
264         VTimescale prec(VTimescale::NONE);
265         bool precbad;
266         prec = VTimescale(precStr, precbad /*ref*/);
267         if (precbad) {
268             fl->v3error("`timescale timeprecision syntax error: '" << precStr << "'");
269             return;
270         }
271         if (!unit.isNone() && !prec.isNone() && unit < prec) {
272             fl->v3error("`timescale timeunit '"
273                         << unitStr << "' must be greater than or equal to timeprecision '"
274                         << precStr << "'");
275             return;
276         }
277         precr = prec;
278     }
279 }
280 
281 //######################################################################
282 // V3Options class functions
283 
addIncDirUser(const string & incdir)284 void V3Options::addIncDirUser(const string& incdir) { m_impp->addIncDirUser(incdir); }
addIncDirFallback(const string & incdir)285 void V3Options::addIncDirFallback(const string& incdir) { m_impp->addIncDirFallback(incdir); }
addLangExt(const string & langext,const V3LangCode & lc)286 void V3Options::addLangExt(const string& langext, const V3LangCode& lc) {
287     m_impp->addLangExt(langext, lc);
288 }
addLibExtV(const string & libext)289 void V3Options::addLibExtV(const string& libext) { m_impp->addLibExtV(libext); }
addDefine(const string & defline,bool allowPlus)290 void V3Options::addDefine(const string& defline, bool allowPlus) {
291     // Split +define+foo=value into the appropriate parts and parse
292     // Optional + says to allow multiple defines on the line
293     // + is not quotable, as other simulators do not allow that
294     string left = defline;
295     while (left != "") {
296         string def = left;
297         string::size_type pos;
298         if (allowPlus && ((pos = left.find('+')) != string::npos)) {
299             left = left.substr(pos + 1);
300             def.erase(pos);
301         } else {
302             left = "";
303         }
304         string value;
305         if ((pos = def.find('=')) != string::npos) {
306             value = def.substr(pos + 1);
307             def.erase(pos);
308         }
309         V3PreShell::defineCmdLine(def, value);
310     }
311 }
addParameter(const string & paramline,bool allowPlus)312 void V3Options::addParameter(const string& paramline, bool allowPlus) {
313     // Split +define+foo=value into the appropriate parts and parse
314     // Optional + says to allow multiple defines on the line
315     // + is not quotable, as other simulators do not allow that
316     string left = paramline;
317     while (left != "") {
318         string param = left;
319         string::size_type pos;
320         if (allowPlus && ((pos = left.find('+')) != string::npos)) {
321             left = left.substr(pos + 1);
322             param.erase(pos);
323         } else {
324             left = "";
325         }
326         string value;
327         if ((pos = param.find('=')) != string::npos) {
328             value = param.substr(pos + 1);
329             param.erase(pos);
330         }
331         UINFO(4, "Add parameter" << param << "=" << value << endl);
332         (void)m_parameters.erase(param);
333         m_parameters[param] = value;
334     }
335 }
336 
hasParameter(const string & name)337 bool V3Options::hasParameter(const string& name) {
338     return m_parameters.find(name) != m_parameters.end();
339 }
340 
parameter(const string & name)341 string V3Options::parameter(const string& name) {
342     const string value = m_parameters.find(name)->second;
343     m_parameters.erase(m_parameters.find(name));
344     return value;
345 }
346 
checkParameters()347 void V3Options::checkParameters() {
348     if (!m_parameters.empty()) {
349         std::stringstream msg;
350         msg << "Parameters from the command line were not found in the design:";
351         for (const auto& i : m_parameters) msg << " " << i.first;
352         v3error(msg.str());
353     }
354 }
355 
addCppFile(const string & filename)356 void V3Options::addCppFile(const string& filename) { m_cppFiles.insert(filename); }
addCFlags(const string & filename)357 void V3Options::addCFlags(const string& filename) { m_cFlags.push_back(filename); }
addLdLibs(const string & filename)358 void V3Options::addLdLibs(const string& filename) { m_ldLibs.push_back(filename); }
addMakeFlags(const string & filename)359 void V3Options::addMakeFlags(const string& filename) { m_makeFlags.push_back(filename); }
addFuture(const string & flag)360 void V3Options::addFuture(const string& flag) { m_futures.insert(flag); }
isFuture(const string & flag) const361 bool V3Options::isFuture(const string& flag) const {
362     return m_futures.find(flag) != m_futures.end();
363 }
isLibraryFile(const string & filename) const364 bool V3Options::isLibraryFile(const string& filename) const {
365     return m_libraryFiles.find(filename) != m_libraryFiles.end();
366 }
addLibraryFile(const string & filename)367 void V3Options::addLibraryFile(const string& filename) { m_libraryFiles.insert(filename); }
isClocker(const string & signame) const368 bool V3Options::isClocker(const string& signame) const {
369     return m_clockers.find(signame) != m_clockers.end();
370 }
addClocker(const string & signame)371 void V3Options::addClocker(const string& signame) { m_clockers.insert(signame); }
isNoClocker(const string & signame) const372 bool V3Options::isNoClocker(const string& signame) const {
373     return m_noClockers.find(signame) != m_noClockers.end();
374 }
addNoClocker(const string & signame)375 void V3Options::addNoClocker(const string& signame) { m_noClockers.insert(signame); }
addVFile(const string & filename)376 void V3Options::addVFile(const string& filename) {
377     // We use a list for v files, because it's legal to have includes
378     // in a specific order and multiple of them.
379     m_vFiles.push_back(filename);
380 }
addForceInc(const string & filename)381 void V3Options::addForceInc(const string& filename) { m_forceIncs.push_back(filename); }
382 
addArg(const string & arg)383 void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); }
384 
allArgsString() const385 string V3Options::allArgsString() const {
386     string out;
387     for (const string& i : m_impp->m_allArgs) {
388         if (out != "") out += " ";
389         out += i;
390     }
391     return out;
392 }
393 
394 // Delete some options for Verilation of the hierarchical blocks.
allArgsStringForHierBlock(bool forTop) const395 string V3Options::allArgsStringForHierBlock(bool forTop) const {
396     std::set<string> vFiles;
397     for (const auto& vFile : m_vFiles) vFiles.insert(vFile);
398     string out;
399     for (std::list<string>::const_iterator it = m_impp->m_allArgs.begin();
400          it != m_impp->m_allArgs.end(); ++it) {
401         int skip = 0;
402         if (it->length() >= 2 && (*it)[0] == '-' && (*it)[1] == '-') {
403             skip = 2;
404         } else if (it->length() >= 1 && (*it)[0] == '-') {
405             skip = 1;
406         }
407         if (skip > 0) {  // *it is an option
408             const string opt = it->substr(skip);  // Remove '-' in the beginning
409             const int numStrip = stripOptionsForChildRun(opt, forTop);
410             if (numStrip) {
411                 UASSERT(0 <= numStrip && numStrip <= 2, "should be one of 0, 1, 2");
412                 if (numStrip == 2) ++it;
413                 continue;
414             }
415         } else {  // Not an option
416             if (vFiles.find(*it) != vFiles.end()  // Remove HDL
417                 || m_cppFiles.find(*it) != m_cppFiles.end()) {  // Remove C++
418                 continue;
419             }
420         }
421         if (out != "") out += " ";
422         // Don't use opt here because '-' is removed in it
423         // Use double quote because *it may contain whitespaces
424         out += '"' + VString::quoteAny(*it, '"', '\\') + '"';
425     }
426     return out;
427 }
428 
429 //######################################################################
430 // File searching
431 
fileStatNormal(const string & filename)432 bool V3Options::fileStatNormal(const string& filename) {
433     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
434     struct stat sstat;  // Stat information
435     const int err = stat(filename.c_str(), &sstat);
436     if (err != 0) return false;
437     if (S_ISDIR(sstat.st_mode)) return false;
438     return true;
439 }
440 
fileNfsFlush(const string & filename)441 void V3Options::fileNfsFlush(const string& filename) {
442     // NFS caches stat() calls so to get up-to-date information must
443     // do a open or opendir on the filename.
444     // Faster to just try both rather than check if a file is a dir.
445     if (DIR* const dirp = opendir(filename.c_str())) {  // LCOV_EXCL_BR_LINE
446         closedir(dirp);  // LCOV_EXCL_LINE
447     } else if (int fd = ::open(filename.c_str(), O_RDONLY)) {  // LCOV_EXCL_BR_LINE
448         if (fd > 0) ::close(fd);
449     }
450 }
451 
fileExists(const string & filename)452 string V3Options::fileExists(const string& filename) {
453     // Surprisingly, for VCS and other simulators, this process
454     // is quite slow; presumably because of re-reading each directory
455     // many times.  So we read a whole dir at once and cache it
456 
457     const string dir = V3Os::filenameDir(filename);
458     const string basename = V3Os::filenameNonDir(filename);
459 
460     auto diriter = m_impp->m_dirMap.find(dir);
461     if (diriter == m_impp->m_dirMap.end()) {
462         // Read the listing
463         m_impp->m_dirMap.emplace(dir, std::set<string>());
464         diriter = m_impp->m_dirMap.find(dir);
465 
466         std::set<string>* setp = &(diriter->second);
467 
468         if (DIR* const dirp = opendir(dir.c_str())) {
469             while (struct dirent* direntp = readdir(dirp)) setp->insert(direntp->d_name);
470             closedir(dirp);
471         }
472     }
473     // Find it
474     const std::set<string>* filesetp = &(diriter->second);
475     const auto fileiter = filesetp->find(basename);
476     if (fileiter == filesetp->end()) {
477         return "";  // Not found
478     }
479     // Check if it is a directory, ignore if so
480     const string filenameOut = V3Os::filenameFromDirBase(dir, basename);
481     if (!fileStatNormal(filenameOut)) return "";  // Directory
482     return filenameOut;
483 }
484 
filePathCheckOneDir(const string & modname,const string & dirname)485 string V3Options::filePathCheckOneDir(const string& modname, const string& dirname) {
486     for (const string& i : m_impp->m_libExtVs) {
487         const string fn = V3Os::filenameFromDirBase(dirname, modname + i);
488         string exists = fileExists(fn);
489         if (exists != "") {
490             // Strip ./, it just looks ugly
491             if (exists.substr(0, 2) == "./") exists.erase(0, 2);
492             return exists;
493         }
494     }
495     return "";
496 }
497 
498 // Checks if a option needs to be stripped for child run of hierarchical Verilation.
499 // 0: Keep the option including its argument
500 // 1: Delete the option which has no argument
501 // 2: Delete the option and its argument
stripOptionsForChildRun(const string & opt,bool forTop)502 int V3Options::stripOptionsForChildRun(const string& opt, bool forTop) {
503     if (opt == "Mdir" || opt == "clk" || opt == "lib-create" || opt == "f" || opt == "j"
504         || opt == "l2-name" || opt == "mod-prefix" || opt == "prefix" || opt == "protect-lib"
505         || opt == "protect-key" || opt == "threads" || opt == "top-module" || opt == "v") {
506         return 2;
507     }
508     if (opt == "build" || (!forTop && (opt == "cc" || opt == "exe" || opt == "sc"))
509         || opt == "hierarchical" || (opt.length() > 2 && opt.substr(0, 2) == "G=")) {
510         return 1;
511     }
512     return 0;
513 }
514 
filePath(FileLine * fl,const string & modname,const string & lastpath,const string & errmsg)515 string V3Options::filePath(FileLine* fl, const string& modname, const string& lastpath,
516                            const string& errmsg) {  // Error prefix or "" to suppress error
517     // Find a filename to read the specified module name,
518     // using the incdir and libext's.
519     // Return "" if not found.
520     for (const string& dir : m_impp->m_incDirUsers) {
521         const string exists = filePathCheckOneDir(modname, dir);
522         if (exists != "") return exists;
523     }
524     for (const string& dir : m_impp->m_incDirFallbacks) {
525         const string exists = filePathCheckOneDir(modname, dir);
526         if (exists != "") return exists;
527     }
528 
529     if (m_relativeIncludes) {
530         const string exists = filePathCheckOneDir(modname, lastpath);
531         if (exists != "") return V3Os::filenameRealPath(exists);
532     }
533 
534     // Warn and return not found
535     if (errmsg != "") {
536         fl->v3error(errmsg + modname);
537         filePathLookedMsg(fl, modname);
538     }
539     return "";
540 }
541 
filePathLookedMsg(FileLine * fl,const string & modname)542 void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
543     static bool shown_notfound_msg = false;
544     if (modname.find("__Vhsh") != string::npos) {
545         std::cerr << V3Error::warnMore() << "... Unsupported: Name is longer than 127 characters;"
546                   << " automatic file lookup not supported.\n";
547         std::cerr << V3Error::warnMore() << "... Suggest putting filename with this module/package"
548                   << " onto command line instead.\n";
549     } else if (!shown_notfound_msg) {
550         shown_notfound_msg = true;
551         if (m_impp->m_incDirUsers.empty()) {
552             fl->v3error("This may be because there's no search path specified with -I<dir>.");
553         }
554         std::cerr << V3Error::warnMore() << "... Looked in:" << endl;
555         for (const string& dir : m_impp->m_incDirUsers) {
556             for (const string& ext : m_impp->m_libExtVs) {
557                 const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
558                 std::cerr << V3Error::warnMore() << "     " << fn << endl;
559             }
560         }
561         for (const string& dir : m_impp->m_incDirFallbacks) {
562             for (const string& ext : m_impp->m_libExtVs) {
563                 const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
564                 std::cerr << V3Error::warnMore() << "     " << fn << endl;
565             }
566         }
567     }
568 }
569 
570 //! Determine what language is associated with a filename
571 
572 //! If we recognize the extension, use its language, otherwise, use the
573 //! default language.
fileLanguage(const string & filename)574 V3LangCode V3Options::fileLanguage(const string& filename) {
575     string ext = V3Os::filenameNonDir(filename);
576     string::size_type pos;
577     if ((pos = ext.rfind('.')) != string::npos) {
578         ext.erase(0, pos + 1);
579         const auto it = m_impp->m_langExts.find(ext);
580         if (it != m_impp->m_langExts.end()) return it->second;
581     }
582     return m_defaultLanguage;
583 }
584 
585 //######################################################################
586 // Environment
587 
getenvBuiltins(const string & var)588 string V3Options::getenvBuiltins(const string& var) {
589     if (var == "MAKE") {
590         return getenvMAKE();
591     } else if (var == "PERL") {
592         return getenvPERL();
593     } else if (var == "SYSTEMC") {
594         return getenvSYSTEMC();
595     } else if (var == "SYSTEMC_ARCH") {
596         return getenvSYSTEMC_ARCH();
597     } else if (var == "SYSTEMC_INCLUDE") {
598         return getenvSYSTEMC_INCLUDE();
599     } else if (var == "SYSTEMC_LIBDIR") {
600         return getenvSYSTEMC_LIBDIR();
601     } else if (var == "VERILATOR_ROOT") {
602         return getenvVERILATOR_ROOT();
603     } else {
604         return V3Os::getenvStr(var, "");
605     }
606 }
607 
608 #ifdef __FreeBSD__
getenvMAKE()609 string V3Options::getenvMAKE() { return V3Os::getenvStr("MAKE", "gmake"); }
610 #else
getenvMAKE()611 string V3Options::getenvMAKE() { return V3Os::getenvStr("MAKE", "make"); }
612 #endif
613 
getenvPERL()614 string V3Options::getenvPERL() {  //
615     return V3Os::getenvStr("PERL", "perl");
616 }
617 
getenvSYSTEMC()618 string V3Options::getenvSYSTEMC() {
619     string var = V3Os::getenvStr("SYSTEMC", "");
620     if (var == "" && string(DEFENV_SYSTEMC) != "") {
621         var = DEFENV_SYSTEMC;
622         V3Os::setenvStr("SYSTEMC", var, "Hardcoded at build time");
623     }
624     return var;
625 }
626 
getenvSYSTEMC_ARCH()627 string V3Options::getenvSYSTEMC_ARCH() {
628     string var = V3Os::getenvStr("SYSTEMC_ARCH", "");
629     if (var == "" && string(DEFENV_SYSTEMC_ARCH) != "") {
630         var = DEFENV_SYSTEMC_ARCH;
631         V3Os::setenvStr("SYSTEMC_ARCH", var, "Hardcoded at build time");
632     }
633     if (var == "") {
634 #if defined(__MINGW32__)
635         // Hardcoded with MINGW current version. Would like a better way.
636         const string sysname = "MINGW32_NT-5.0";
637         var = "mingw32";
638 #elif defined(_WIN32)
639         const string sysname = "WIN32";
640         var = "win32";
641 #else
642         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
643         struct utsname uts;
644         uname(&uts);
645         const string sysname = VString::downcase(uts.sysname);  // aka  'uname -s'
646         if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*solaris*"))) {
647             var = "gccsparcOS5";
648         } else if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*cygwin*"))) {
649             var = "cygwin";
650         } else {
651             var = "linux";
652         }
653 #endif
654         V3Os::setenvStr("SYSTEMC_ARCH", var, "From sysname '" + sysname + "'");
655     }
656     return var;
657 }
658 
getenvSYSTEMC_INCLUDE()659 string V3Options::getenvSYSTEMC_INCLUDE() {
660     string var = V3Os::getenvStr("SYSTEMC_INCLUDE", "");
661     if (var == "" && string(DEFENV_SYSTEMC_INCLUDE) != "") {
662         var = DEFENV_SYSTEMC_INCLUDE;
663         V3Os::setenvStr("SYSTEMC_INCLUDE", var, "Hardcoded at build time");
664     }
665     if (var == "") {
666         const string sc = getenvSYSTEMC();
667         if (sc != "") var = sc + "/include";
668     }
669     return var;
670 }
671 
getenvSYSTEMC_LIBDIR()672 string V3Options::getenvSYSTEMC_LIBDIR() {
673     string var = V3Os::getenvStr("SYSTEMC_LIBDIR", "");
674     if (var == "" && string(DEFENV_SYSTEMC_LIBDIR) != "") {
675         var = DEFENV_SYSTEMC_LIBDIR;
676         V3Os::setenvStr("SYSTEMC_LIBDIR", var, "Hardcoded at build time");
677     }
678     if (var == "") {
679         const string sc = getenvSYSTEMC();
680         const string arch = getenvSYSTEMC_ARCH();
681         if (sc != "" && arch != "") var = sc + "/lib-" + arch;
682     }
683     return var;
684 }
685 
getenvVERILATOR_ROOT()686 string V3Options::getenvVERILATOR_ROOT() {
687     string var = V3Os::getenvStr("VERILATOR_ROOT", "");
688     if (var == "" && string(DEFENV_VERILATOR_ROOT) != "") {
689         var = DEFENV_VERILATOR_ROOT;
690         V3Os::setenvStr("VERILATOR_ROOT", var, "Hardcoded at build time");
691     }
692     if (var == "") v3fatal("$VERILATOR_ROOT needs to be in environment\n");
693     return var;
694 }
695 
systemCSystemWide()696 bool V3Options::systemCSystemWide() {
697 #ifdef HAVE_SYSTEMC
698     return true;
699 #else
700     return false;
701 #endif
702 }
703 
systemCFound()704 bool V3Options::systemCFound() {
705     return (systemCSystemWide()
706             || (!getenvSYSTEMC_INCLUDE().empty() && !getenvSYSTEMC_LIBDIR().empty()));
707 }
708 
709 //######################################################################
710 // V3 Options notification methods
711 
notify()712 void V3Options::notify() {
713     FileLine* const cmdfl = new FileLine(FileLine::commandLineFilename());
714 
715     // Notify that all arguments have been passed and final modification can be made.
716     if (!outFormatOk() && !cdc() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) {
717         v3fatal("verilator: Need --cc, --sc, --cdc, --dpi-hdr-only, --lint-only, "
718                 "--xml-only or --E option");
719     }
720 
721     if (m_build && (m_gmake || m_cmake)) {
722         cmdfl->v3error("--make cannot be used together with --build. Suggest see manual");
723     }
724 
725     // Make sure at least one make system is enabled
726     if (!m_gmake && !m_cmake) m_gmake = true;
727 
728     if (m_hierarchical && (m_hierChild || !m_hierBlocks.empty())) {
729         cmdfl->v3error(
730             "--hierarchical must not be set with --hierarchical-child or --hierarchical-block");
731     }
732     if (m_hierChild && m_hierBlocks.empty()) {
733         cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set");
734     }
735 
736     if (protectIds()) {
737         if (allPublic()) {
738             // We always call protect() on names, we don't check if public or not
739             // Hence any external references wouldn't be able to find the refed public object.
740             cmdfl->v3warn(E_UNSUPPORTED, "Unsupported: Using --protect-ids with --public\n"  //
741                                              + V3Error::warnMore()
742                                              + "... Suggest remove --public.");
743         }
744         if (trace()) {
745             cmdfl->v3warn(INSECURE,
746                           "Using --protect-ids with --trace may expose private design details\n"
747                               + V3Error::warnMore() + "... Suggest remove --trace.");
748         }
749         if (vpi()) {
750             cmdfl->v3warn(INSECURE,
751                           "Using --protect-ids with --vpi may expose private design details\n"
752                               + V3Error::warnMore() + "... Suggest remove --vpi.");
753         }
754     }
755 
756     // Default some options if not turned on or off
757     if (v3Global.opt.skipIdentical().isDefault()) {
758         v3Global.opt.m_skipIdentical.setTrueOrFalse(  //
759             !v3Global.opt.cdc()  //
760             && !v3Global.opt.dpiHdrOnly()  //
761             && !v3Global.opt.lintOnly()  //
762             && !v3Global.opt.preprocOnly()  //
763             && !v3Global.opt.xmlOnly());
764     }
765     if (v3Global.opt.makeDepend().isDefault()) {
766         v3Global.opt.m_makeDepend.setTrueOrFalse(  //
767             !v3Global.opt.cdc()  //
768             && !v3Global.opt.dpiHdrOnly()  //
769             && !v3Global.opt.lintOnly()  //
770             && !v3Global.opt.preprocOnly()  //
771             && !v3Global.opt.xmlOnly());
772     }
773 
774     // --trace-threads implies --threads 1 unless explicitly specified
775     if (traceThreads() && !threads()) m_threads = 1;
776 
777     // Default split limits if not specified
778     if (m_outputSplitCFuncs < 0) m_outputSplitCFuncs = m_outputSplit;
779     if (m_outputSplitCTrace < 0) m_outputSplitCTrace = m_outputSplit;
780 
781     if (v3Global.opt.main() && v3Global.opt.systemC()) {
782         cmdfl->v3warn(E_UNSUPPORTED,
783                       "--main not usable with SystemC. Suggest see examples for sc_main().");
784     }
785 
786     if (coverage() && savable()) {
787         cmdfl->v3error("--coverage and --savable not supported together");
788     }
789 
790     // Mark options as available
791     m_available = true;
792 }
793 
794 //######################################################################
795 // V3 Options accessors
796 
version()797 string V3Options::version() {
798     string ver = DTVERSION;
799     ver += " rev " + cvtToStr(DTVERSION_rev);
800     return ver;
801 }
802 
protectKeyDefaulted()803 string V3Options::protectKeyDefaulted() {
804     if (m_protectKey.empty()) {
805         // Create a key with a human-readable symbol-like name.
806         // This conversion drops ~2 bits of entropy out of 256, shouldn't matter.
807         VHashSha256 digest(V3Os::trueRandom(32));
808         m_protectKey = "VL-KEY-" + digest.digestSymbol();
809     }
810     return m_protectKey;
811 }
812 
throwSigsegv()813 void V3Options::throwSigsegv() {  // LCOV_EXCL_START
814 #if !(defined(VL_CPPCHECK) || defined(__clang_analyzer__))
815     // clang-format off
816     *static_cast<volatile char*>(nullptr) = 0;  // Intentional core dump, ignore warnings here
817     // clang-format on
818 #endif
819 }  // LCOV_EXCL_STOP
820 
timeComputePrec(const VTimescale & flag) const821 VTimescale V3Options::timeComputePrec(const VTimescale& flag) const {
822     if (!timeOverridePrec().isNone()) {
823         return timeOverridePrec();
824     } else if (flag.isNone()) {
825         return timeDefaultPrec();
826     } else {
827         return flag;
828     }
829 }
830 
timeComputeUnit(const VTimescale & flag) const831 VTimescale V3Options::timeComputeUnit(const VTimescale& flag) const {
832     if (!timeOverrideUnit().isNone()) {
833         return timeOverrideUnit();
834     } else if (flag.isNone()) {
835         return timeDefaultUnit();
836     } else {
837         return flag;
838     }
839 }
840 
841 //######################################################################
842 // V3 Options utilities
843 
argString(int argc,char ** argv)844 string V3Options::argString(int argc, char** argv) {
845     // Return list of arguments as simple string
846     string opts;
847     for (int i = 0; i < argc; ++i) {
848         if (i != 0) opts += " ";
849         opts += string(argv[i]);
850     }
851     return opts;
852 }
853 
854 //######################################################################
855 // V3 Options Parsing
856 
parseOpts(FileLine * fl,int argc,char ** argv)857 void V3Options::parseOpts(FileLine* fl, int argc, char** argv) {
858     // Parse all options
859     // Initial entry point from Verilator.cpp
860     parseOptsList(fl, ".", argc, argv);
861 
862     // Default certain options and error check
863     // Detailed error, since this is what we often get when run with minimal arguments
864     const V3StringList& vFilesList = vFiles();
865     if (vFilesList.empty()) {
866         v3fatal("verilator: No Input Verilog file specified on command line, "
867                 "see verilator --help for more information\n");
868     }
869 
870     // Default prefix to the filename
871     if (prefix() == "" && topModule() != "")
872         m_prefix = string("V") + AstNode::encodeName(topModule());
873     if (prefix() == "" && vFilesList.size() >= 1)
874         m_prefix = string("V") + AstNode::encodeName(V3Os::filenameNonExt(*(vFilesList.begin())));
875     if (modPrefix() == "") m_modPrefix = prefix();
876 
877     // Find files in makedir
878     addIncDirFallback(makeDir());
879 }
880 
881 //======================================================================
882 
suffixed(const string & sw,const char * arg)883 bool V3Options::suffixed(const string& sw, const char* arg) {
884     if (strlen(arg) > sw.length()) return false;
885     return (0 == strcmp(sw.c_str() + sw.length() - strlen(arg), arg));
886 }
887 
parseOptsList(FileLine * fl,const string & optdir,int argc,char ** argv)888 void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv) {
889     // Parse parameters
890     // Note argc and argv DO NOT INCLUDE the filename in [0]!!!
891     // May be called recursively when there are -f files.
892     for (int i = 0; i < argc; ++i) {
893         addArg(argv[i]);  // -f's really should be inserted in the middle, but this is for debug
894     }
895 
896     V3OptionParser parser;
897     const V3OptionParser::AppendHelper DECL_OPTION{parser};
898     V3OPTION_PARSER_DECL_TAGS;
899 
900     const auto callStrSetter = [this](void (V3Options::*cbStr)(const string&)) {
901         return [this, cbStr](const string& v) { (this->*cbStr)(v); };
902     };
903     // Usage
904     // DECL_OPTION("-option", action, pointer_or_lambda);
905     // action: one of Set, OnOff, CbCall, CbOnOff, CbVal, CbPartialMatch, and CbPartialMatchVal
906     //   Set              : Set value to a variable, pointer_or_lambda must be a pointer to the
907     //                      variable.
908     //                      true is set to bool-ish variable when '-opt' is passed to verilator.
909     //                      val is set to int and string variable when '-opt val' is passed.
910     //   OnOff            : Set value to a bool-ish variable, pointer_or_lambda must be a pointer
911     //                      to bool or VOptionBool.
912     //                      true is set if "-opt" is passed to verilator while false is set if
913     //                      "-no-opt" is given.
914     //   CbCall           : Call lambda or function that does not take argument.
915     //   CbOnOff          : Call lambda or function that takes bool argument.
916     //                      Supports "-opt" and "-no-opt" style options.
917     //   CbVal            : Call lambda or function that takes int or const char*.
918     //                      "-opt val" is passed to verilator, val is passed to the lambda.
919     //                      If a function to be called is a member of V3Options that only takes
920     //                      const string&, callStrSetter(&V3Options::memberFunc) can be passed
921     //                      instead of lambda as a syntax sugar.
922     //   CbPartialMatch   : Call lambda or function that takes remaining string.
923     //                      e.g. DECL_OPTION("-opt-", CbPartialMatch, [](const char*optp) { cout <<
924     //                      optp << endl; }); and "-opt-ABC" is passed, "ABC" will be emit to
925     //                      stdout.
926     //   CbPartialMatchVal: Call lambda or function that takes remaining string and value.
927     //                      e.g. DECL_OPTION("-opt-", CbPartialMatchVal, [](const char*optp, const
928     //                      char*valp) {
929     //                               cout << optp << ":" << valp << endl; });
930     //                      and "-opt-ABC VAL" is passed, "ABC:VAL" will be emit to stdout.
931     //
932     // DECL_OPTION is not C-macro to get correct line coverage even when lambda is passed.
933     // (If DECL_OPTION is a macro, then lambda would be collapsed into a single line).
934 
935     // Plus options
936     DECL_OPTION("+define+", CbPartialMatch, [this](const char* optp) { addDefine(optp, true); });
937     DECL_OPTION("+incdir+", CbPartialMatch,
938                 [this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
939     DECL_OPTION("+libext+", CbPartialMatch, [this](const char* optp) {
940         string exts = optp;
941         string::size_type pos;
942         while ((pos = exts.find('+')) != string::npos) {
943             addLibExtV(exts.substr(0, pos));
944             exts = exts.substr(pos + 1);
945         }
946         addLibExtV(exts);
947     });
948     DECL_OPTION("+librescan", CbCall, []() {});  // NOP
949     DECL_OPTION("+notimingchecks", CbCall, []() {});  // NOP
950     DECL_OPTION("+systemverilogext+", CbPartialMatch,
951                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2017); });
952     DECL_OPTION("+verilog1995ext+", CbPartialMatch,
953                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1364_1995); });
954     DECL_OPTION("+verilog2001ext+", CbPartialMatch,
955                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1364_2001); });
956     DECL_OPTION("+1364-1995ext+", CbPartialMatch,
957                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1364_1995); });
958     DECL_OPTION("+1364-2001ext+", CbPartialMatch,
959                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1364_2001); });
960     DECL_OPTION("+1364-2005ext+", CbPartialMatch,
961                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1364_2005); });
962     DECL_OPTION("+1800-2005ext+", CbPartialMatch,
963                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2005); });
964     DECL_OPTION("+1800-2009ext+", CbPartialMatch,
965                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2009); });
966     DECL_OPTION("+1800-2012ext+", CbPartialMatch,
967                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2012); });
968     DECL_OPTION("+1800-2017ext+", CbPartialMatch,
969                 [this](const char* optp) { addLangExt(optp, V3LangCode::L1800_2017); });
970 
971     // Minus options
972     DECL_OPTION("-assert", OnOff, &m_assert);
973     DECL_OPTION("-autoflush", OnOff, &m_autoflush);
974 
975     DECL_OPTION("-bbox-sys", OnOff, &m_bboxSys);
976     DECL_OPTION("-bbox-unsup", CbOnOff, [this](bool flag) {
977         m_bboxUnsup = flag;
978         FileLine::globalWarnOff(V3ErrorCode::E_UNSUPPORTED, true);
979     });
980     DECL_OPTION("-bin", Set, &m_bin);
981     DECL_OPTION("-build", Set, &m_build);
982 
983     DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags));
984     DECL_OPTION("-cc", CbCall, [this]() {
985         m_outFormatOk = true;
986         m_systemC = false;
987     });
988     DECL_OPTION("-cdc", OnOff, &m_cdc);
989     DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker));
990     DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker));
991     DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented();
992     DECL_OPTION("-comp-limit-members", Set,
993                 &m_compLimitMembers)
994         .undocumented();  // Ideally power-of-two so structs stay aligned
995     DECL_OPTION("-comp-limit-parens", Set, &m_compLimitParens).undocumented();
996     DECL_OPTION("-comp-limit-syms", CbVal, [](int val) { VName::maxLength(val); }).undocumented();
997     DECL_OPTION("-compiler", CbVal, [this, fl](const char* valp) {
998         if (!strcmp(valp, "clang")) {
999             m_compLimitBlocks = 80;  // limit unknown
1000             m_compLimitMembers = 64;  // soft limit, has slowdown bug as of clang++ 3.8
1001             m_compLimitParens = 80;  // limit unknown
1002         } else if (!strcmp(valp, "gcc")) {
1003             m_compLimitBlocks = 0;  // Bug free
1004             m_compLimitMembers = 64;  // soft limit, has slowdown bug as of g++ 7.1
1005             m_compLimitParens = 0;  // Bug free
1006         } else if (!strcmp(valp, "msvc")) {
1007             m_compLimitBlocks = 80;  // 128, but allow some room
1008             m_compLimitMembers = 0;  // probably ok, and AFAIK doesn't support anon structs
1009             m_compLimitParens = 80;  // 128, but allow some room
1010         } else {
1011             fl->v3fatal("Unknown setting for --compiler: '"
1012                         << valp << "'\n"
1013                         << fl->warnMore() << "... Suggest 'clang', 'gcc', or 'msvc'");
1014         }
1015     });
1016     DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); });
1017     DECL_OPTION("-converge-limit", Set, &m_convergeLimit);
1018     DECL_OPTION("-coverage-line", OnOff, &m_coverageLine);
1019     DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth);
1020     DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle);
1021     DECL_OPTION("-coverage-underscore", OnOff, &m_coverageUnderscore);
1022     DECL_OPTION("-coverage-user", OnOff, &m_coverageUser);
1023 
1024     DECL_OPTION("-D", CbPartialMatch, [this](const char* valp) { addDefine(valp, false); });
1025     DECL_OPTION("-debug", CbCall, [this]() { setDebugMode(3); });
1026     DECL_OPTION("-debugi", CbVal, [this](int v) { setDebugMode(v); });
1027     DECL_OPTION("-debugi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
1028         setDebugSrcLevel(optp, std::atoi(valp));
1029     });
1030     DECL_OPTION("-debug-abort", CbCall,
1031                 V3Error::vlAbort)
1032         .undocumented();  // See also --debug-sigsegv
1033     DECL_OPTION("-debug-check", OnOff, &m_debugCheck);
1034     DECL_OPTION("-debug-collision", OnOff, &m_debugCollision).undocumented();
1035     DECL_OPTION("-debug-emitv", OnOff, &m_debugEmitV).undocumented();
1036     DECL_OPTION("-debug-exit-parse", OnOff, &m_debugExitParse).undocumented();
1037     DECL_OPTION("-debug-exit-uvm", OnOff, &m_debugExitUvm).undocumented();
1038     DECL_OPTION("-debug-fatalsrc", CbCall, []() {
1039         v3fatalSrc("--debug-fatal-src");
1040     }).undocumented();  // See also --debug-abort
1041     DECL_OPTION("-debug-leak", OnOff, &m_debugLeak);
1042     DECL_OPTION("-debug-nondeterminism", OnOff, &m_debugNondeterminism);
1043     DECL_OPTION("-debug-partition", OnOff, &m_debugPartition).undocumented();
1044     DECL_OPTION("-debug-protect", OnOff, &m_debugProtect).undocumented();
1045     DECL_OPTION("-debug-self-test", OnOff, &m_debugSelfTest).undocumented();
1046     DECL_OPTION("-debug-sigsegv", CbCall, throwSigsegv).undocumented();  // See also --debug-abort
1047     DECL_OPTION("-decoration", OnOff, &m_decoration);
1048     DECL_OPTION("-dpi-hdr-only", OnOff, &m_dpiHdrOnly);
1049     DECL_OPTION("-dump-defines", OnOff, &m_dumpDefines);
1050     DECL_OPTION("-dump-tree", CbOnOff,
1051                 [this](bool flag) { m_dumpTree = flag ? 3 : 0; });  // Also see --dump-treei
1052     DECL_OPTION("-dump-tree-addrids", OnOff, &m_dumpTreeAddrids);
1053     DECL_OPTION("-dump-treei", Set, &m_dumpTree);
1054     DECL_OPTION("-dump-treei-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
1055         setDumpTreeLevel(optp, std::atoi(valp));
1056     });
1057 
1058     DECL_OPTION("-E", Set, &m_preprocOnly);
1059     DECL_OPTION("-error-limit", CbVal, static_cast<void (*)(int)>(&V3Error::errorLimit));
1060     DECL_OPTION("-exe", OnOff, &m_exe);
1061     DECL_OPTION("-expand-limit", CbVal,
1062                 [this](const char* valp) { m_expandLimit = std::atoi(valp); });
1063 
1064     DECL_OPTION("-F", CbVal, [this, fl, &optdir](const char* valp) {
1065         parseOptsFile(fl, parseFileArg(optdir, valp), true);
1066     });
1067     DECL_OPTION("-FI", CbVal,
1068                 [this, &optdir](const char* valp) { addForceInc(parseFileArg(optdir, valp)); });
1069     DECL_OPTION("-f", CbVal, [this, fl, &optdir](const char* valp) {
1070         parseOptsFile(fl, parseFileArg(optdir, valp), false);
1071     });
1072     DECL_OPTION("-flatten", OnOff, &m_flatten);
1073 
1074     DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); });
1075     DECL_OPTION("-gate-stmts", Set, &m_gateStmts);
1076     DECL_OPTION("-gdb", CbCall, []() {});  // Processed only in bin/verilator shell
1077     DECL_OPTION("-gdbbt", CbCall, []() {});  // Processed only in bin/verilator shell
1078     DECL_OPTION("-generate-key", CbCall, [this]() {
1079         cout << protectKeyDefaulted() << endl;
1080         std::exit(0);
1081     });
1082     DECL_OPTION("-getenv", CbVal, [](const char* valp) {
1083         cout << V3Options::getenvBuiltins(valp) << endl;
1084         std::exit(0);
1085     });
1086 
1087     DECL_OPTION("-hierarchical", OnOff, &m_hierarchical);
1088     DECL_OPTION("-hierarchical-block", CbVal, [this](const char* valp) {
1089         const V3HierarchicalBlockOption opt(valp);
1090         m_hierBlocks.emplace(opt.mangledName(), opt);
1091     });
1092     DECL_OPTION("-hierarchical-child", OnOff, &m_hierChild);
1093 
1094     DECL_OPTION("-I", CbPartialMatch,
1095                 [this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
1096     DECL_OPTION("-if-depth", Set, &m_ifDepth);
1097     DECL_OPTION("-ignc", OnOff, &m_ignc);
1098     DECL_OPTION("-inline-mult", Set, &m_inlineMult);
1099     DECL_OPTION("-instr-count-dpi", CbVal, [this, fl](int val) {
1100         m_instrCountDpi = val;
1101         if (m_instrCountDpi < 0) fl->v3fatal("--instr-count-dpi must be non-negative: " << val);
1102     });
1103 
1104     DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs));
1105     const auto setLang = [this, fl](const char* valp) {
1106         const V3LangCode optval = V3LangCode(valp);
1107         if (optval.legal()) {
1108             m_defaultLanguage = optval;
1109         } else {
1110             VSpellCheck spell;
1111             for (int i = V3LangCode::L_ERROR + 1; i < V3LangCode::_ENUM_END; ++i) {
1112                 spell.pushCandidate(V3LangCode{i}.ascii());
1113             }
1114             fl->v3fatal("Unknown language specified: " << valp << spell.bestCandidateMsg(valp));
1115         }
1116     };
1117     DECL_OPTION("-default-language", CbVal, setLang);
1118     DECL_OPTION("-language", CbVal, setLang);
1119     DECL_OPTION("-lib-create", Set, &m_libCreate);
1120     DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
1121     DECL_OPTION("-l2-name", Set, &m_l2Name);
1122     DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented();  // Historical
1123     DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented();  // Historical
1124 
1125     DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
1126     DECL_OPTION("-MMD", OnOff, &m_makeDepend);
1127     DECL_OPTION("-MP", OnOff, &m_makePhony);
1128     DECL_OPTION("-Mdir", CbVal, [this](const char* valp) {
1129         m_makeDir = valp;
1130         addIncDirFallback(m_makeDir);  // Need to find generated files there too
1131     });
1132     DECL_OPTION("-main", OnOff, &m_main).undocumented();  // Future
1133     DECL_OPTION("-make", CbVal, [this, fl](const char* valp) {
1134         if (!strcmp(valp, "cmake")) {
1135             m_cmake = true;
1136         } else if (!strcmp(valp, "gmake")) {
1137             m_gmake = true;
1138         } else {
1139             fl->v3fatal("Unknown --make system specified: '" << valp << "'");
1140         }
1141     });
1142     DECL_OPTION("-max-num-width", Set, &m_maxNumWidth);
1143     DECL_OPTION("-merge-const-pool", OnOff, &m_mergeConstPool);
1144     DECL_OPTION("-mod-prefix", Set, &m_modPrefix);
1145 
1146     DECL_OPTION("-O", CbPartialMatch, [this](const char* optp) {
1147         // Optimization
1148         for (const char* cp = optp; *cp; ++cp) {
1149             const bool flag = isupper(*cp);
1150             switch (tolower(*cp)) {
1151             case '0': optimize(0); break;  // 0=all off
1152             case '1': optimize(1); break;  // 1=all on
1153             case '2': optimize(2); break;  // 2=not used
1154             case '3': optimize(3); break;  // 3=high
1155             case 'a': m_oTable = flag; break;
1156             case 'b': m_oCombine = flag; break;
1157             case 'c': m_oConst = flag; break;
1158             case 'd': m_oDedupe = flag; break;
1159             case 'e': m_oCase = flag; break;
1160             //    f
1161             case 'g': m_oGate = flag; break;
1162             //    h
1163             case 'i': m_oInline = flag; break;
1164             //    j
1165             case 'k': m_oSubstConst = flag; break;
1166             case 'l': m_oLife = flag; break;
1167             case 'm': m_oAssemble = flag; break;
1168             //    n
1169             case 'o': m_oConstBitOpTree = flag; break;  // Can remove ~2022-01 when stable
1170             case 'p':
1171                 m_public = !flag;
1172                 break;  // With -Op so flag=0, we want public on so few optimizations done
1173             //    q
1174             case 'r': m_oReorder = flag; break;
1175             case 's': m_oSplit = flag; break;
1176             case 't': m_oLifePost = flag; break;
1177             case 'u': m_oSubst = flag; break;
1178             case 'v': m_oReloop = flag; break;
1179             case 'w': m_oMergeCond = flag; break;
1180             case 'x': m_oExpand = flag; break;
1181             case 'y': m_oAcycSimp = flag; break;
1182             case 'z': m_oLocalize = flag; break;
1183             default: break;  // No error, just ignore
1184             }
1185         }
1186     });
1187     DECL_OPTION("-o", Set, &m_exeName);
1188     DECL_OPTION("-order-clock-delay", OnOff, &m_orderClockDly);
1189     DECL_OPTION("-output-split", Set, &m_outputSplit);
1190     DECL_OPTION("-output-split-cfuncs", CbVal, [this, fl](const char* valp) {
1191         m_outputSplitCFuncs = std::atoi(valp);
1192         if (m_outputSplitCFuncs < 0) {
1193             fl->v3error("--output-split-cfuncs must be >= 0: " << valp);
1194         }
1195     });
1196     DECL_OPTION("-output-split-ctrace", CbVal, [this, fl](const char* valp) {
1197         m_outputSplitCTrace = std::atoi(valp);
1198         if (m_outputSplitCTrace < 0) {
1199             fl->v3error("--output-split-ctrace must be >= 0: " << valp);
1200         }
1201     });
1202 
1203     DECL_OPTION("-P", Set, &m_preprocNoLine);
1204     DECL_OPTION("-pvalue+", CbPartialMatch,
1205                 [this](const char* varp) { addParameter(varp, false); });
1206     DECL_OPTION("-pins64", CbCall, [this]() { m_pinsBv = 65; });
1207     DECL_OPTION("-no-pins64", CbCall, [this]() { m_pinsBv = 33; });
1208     DECL_OPTION("-pins-bv", CbVal, [this, fl](const char* valp) {
1209         m_pinsBv = std::atoi(valp);
1210         if (m_pinsBv > 65) fl->v3fatal("--pins-bv maximum is 65: " << valp);
1211     });
1212     DECL_OPTION("-pins-sc-uint", CbOnOff, [this](bool flag) {
1213         m_pinsScUint = flag;
1214         if (!m_pinsScBigUint) m_pinsBv = 65;
1215     });
1216     DECL_OPTION("-pins-sc-biguint", CbOnOff, [this](bool flag) {
1217         m_pinsScBigUint = flag;
1218         m_pinsBv = 513;
1219     });
1220     DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8);
1221     DECL_OPTION("-pipe-filter", Set, &m_pipeFilter);
1222     DECL_OPTION("-pp-comments", OnOff, &m_ppComments);
1223     DECL_OPTION("-prefix", CbVal, [this](const char* valp) {
1224         m_prefix = valp;
1225         if (m_modPrefix == "") m_modPrefix = m_prefix;
1226     });
1227     DECL_OPTION("-private", CbCall, [this]() { m_public = false; });
1228     DECL_OPTION("-prof-c", OnOff, &m_profC);
1229     DECL_OPTION("-prof-cfuncs", CbCall, [this]() { m_profC = m_profCFuncs = true; });
1230     DECL_OPTION("-profile-cfuncs", CbCall,
1231                 [this]() { m_profC = m_profCFuncs = true; });  // Renamed
1232     DECL_OPTION("-prof-threads", OnOff, &m_profThreads);
1233     DECL_OPTION("-protect-ids", OnOff, &m_protectIds);
1234     DECL_OPTION("-protect-key", Set, &m_protectKey);
1235     DECL_OPTION("-protect-lib", CbVal, [this](const char* valp) {
1236         m_libCreate = valp;
1237         m_protectIds = true;
1238     });
1239     DECL_OPTION("-public", OnOff, &m_public);
1240     DECL_OPTION("-public-flat-rw", CbOnOff, [this](bool flag) {
1241         m_publicFlatRW = flag;
1242         v3Global.dpi(true);
1243     });
1244 
1245     DECL_OPTION("-quiet-exit", OnOff, &m_quietExit);
1246 
1247     DECL_OPTION("-relative-includes", OnOff, &m_relativeIncludes);
1248     DECL_OPTION("-reloop-limit", CbVal, [this, fl](const char* valp) {
1249         m_reloopLimit = std::atoi(valp);
1250         if (m_reloopLimit < 2) { fl->v3error("--reloop-limit must be >= 2: " << valp); }
1251     });
1252     DECL_OPTION("-report-unoptflat", OnOff, &m_reportUnoptflat);
1253     DECL_OPTION("-rr", CbCall, []() {});  // Processed only in bin/verilator shell
1254 
1255     DECL_OPTION("-savable", OnOff, &m_savable);
1256     DECL_OPTION("-sc", CbCall, [this]() {
1257         m_outFormatOk = true;
1258         m_systemC = true;
1259     });
1260     DECL_OPTION("-skip-identical", OnOff, &m_skipIdentical);
1261     DECL_OPTION("-stats", OnOff, &m_stats);
1262     DECL_OPTION("-stats-vars", CbOnOff, [this](bool flag) {
1263         m_statsVars = flag;
1264         m_stats |= flag;
1265     });
1266     DECL_OPTION("-structs-unpacked", OnOff, &m_structsPacked);
1267     DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2017; });
1268 
1269     DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented();  // Debug
1270     DECL_OPTION("-no-threads", CbCall, [this]() { m_threads = 0; });
1271     DECL_OPTION("-threads", CbVal, [this, fl](const char* valp) {
1272         m_threads = std::atoi(valp);
1273         if (m_threads < 0) fl->v3fatal("--threads must be >= 0: " << valp);
1274     });
1275     DECL_OPTION("-threads-dpi", CbVal, [this, fl](const char* valp) {
1276         if (!strcmp(valp, "all")) {
1277             m_threadsDpiPure = true;
1278             m_threadsDpiUnpure = true;
1279         } else if (!strcmp(valp, "none")) {
1280             m_threadsDpiPure = false;
1281             m_threadsDpiUnpure = false;
1282         } else if (!strcmp(valp, "pure")) {
1283             m_threadsDpiPure = true;
1284             m_threadsDpiUnpure = false;
1285         } else {
1286             fl->v3fatal("Unknown setting for --threads-dpi: '"
1287                         << valp << "'\n"
1288                         << fl->warnMore() << "... Suggest 'all', 'none', or 'pure'");
1289         }
1290     });
1291     DECL_OPTION("-threads-max-mtasks", CbVal, [this, fl](const char* valp) {
1292         m_threadsMaxMTasks = std::atoi(valp);
1293         if (m_threadsMaxMTasks < 1) fl->v3fatal("--threads-max-mtasks must be >= 1: " << valp);
1294     });
1295     DECL_OPTION("-timescale", CbVal, [this, fl](const char* valp) {
1296         VTimescale unit;
1297         VTimescale prec;
1298         VTimescale::parseSlashed(fl, valp, unit /*ref*/, prec /*ref*/);
1299         if (!unit.isNone() && timeOverrideUnit().isNone()) m_timeDefaultUnit = unit;
1300         if (!prec.isNone() && timeOverridePrec().isNone()) m_timeDefaultPrec = prec;
1301     });
1302     DECL_OPTION("-timescale-override", CbVal, [this, fl](const char* valp) {
1303         VTimescale unit;
1304         VTimescale prec;
1305         VTimescale::parseSlashed(fl, valp, unit /*ref*/, prec /*ref*/, true);
1306         if (!unit.isNone()) {
1307             m_timeDefaultUnit = unit;
1308             m_timeOverrideUnit = unit;
1309         }
1310         if (!prec.isNone()) {
1311             m_timeDefaultPrec = prec;
1312             m_timeOverridePrec = prec;
1313         }
1314     });
1315     DECL_OPTION("-top-module", Set, &m_topModule);
1316     DECL_OPTION("-top", Set, &m_topModule);
1317     DECL_OPTION("-trace", OnOff, &m_trace);
1318     DECL_OPTION("-trace-coverage", OnOff, &m_traceCoverage);
1319     DECL_OPTION("-trace-depth", Set, &m_traceDepth);
1320     DECL_OPTION("-trace-fst", CbCall, [this]() {
1321         m_trace = true;
1322         m_traceFormat = TraceFormat::FST;
1323         addLdLibs("-lz");
1324     });
1325     DECL_OPTION("-trace-fst-thread", CbCall, [this, fl]() {
1326         m_trace = true;
1327         m_traceFormat = TraceFormat::FST;
1328         addLdLibs("-lz");
1329         fl->v3warn(DEPRECATED, "Option --trace-fst-thread is deprecated. "
1330                                "Use --trace-fst with --trace-threads > 0.");
1331         if (m_traceThreads == 0) m_traceThreads = 1;
1332     });
1333     DECL_OPTION("-trace-max-array", Set, &m_traceMaxArray);
1334     DECL_OPTION("-trace-max-width", Set, &m_traceMaxWidth);
1335     DECL_OPTION("-trace-params", OnOff, &m_traceParams);
1336     DECL_OPTION("-trace-structs", OnOff, &m_traceStructs);
1337     DECL_OPTION("-trace-threads", CbVal, [this, fl](const char* valp) {
1338         m_trace = true;
1339         m_traceThreads = std::atoi(valp);
1340         if (m_traceThreads < 0) fl->v3fatal("--trace-threads must be >= 0: " << valp);
1341     });
1342     DECL_OPTION("-trace-underscore", OnOff, &m_traceUnderscore);
1343 
1344     DECL_OPTION("-U", CbPartialMatch, &V3PreShell::undef);
1345     DECL_OPTION("-underline-zero", OnOff, &m_underlineZero);  // Deprecated
1346     DECL_OPTION("-unroll-count", Set, &m_unrollCount).undocumented();  // Optimization tweak
1347     DECL_OPTION("-unroll-stmts", Set, &m_unrollStmts).undocumented();  // Optimization tweak
1348     DECL_OPTION("-unused-regexp", Set, &m_unusedRegexp);
1349 
1350     DECL_OPTION("-V", CbCall, [this]() {
1351         showVersion(true);
1352         std::exit(0);
1353     });
1354     DECL_OPTION("-v", CbVal, [this, &optdir](const char* valp) {
1355         V3Options::addLibraryFile(parseFileArg(optdir, valp));
1356     });
1357     DECL_OPTION("-verilate", OnOff, &m_verilate);
1358     DECL_OPTION("-version", CbCall, [this]() {
1359         showVersion(false);
1360         std::exit(0);
1361     });
1362     DECL_OPTION("-vpi", OnOff, &m_vpi);
1363 
1364     DECL_OPTION("-Wpedantic", OnOff, &m_pedantic);
1365     DECL_OPTION("-Wall", CbCall, []() {
1366         FileLine::globalWarnLintOff(false);
1367         FileLine::globalWarnStyleOff(false);
1368     });
1369     DECL_OPTION("-Werror-", CbPartialMatch, [this, fl](const char* optp) {
1370         const V3ErrorCode code(optp);
1371         if (code == V3ErrorCode::EC_ERROR) {
1372             if (!isFuture(optp)) fl->v3fatal("Unknown warning specified: -Werror-" << optp);
1373         } else {
1374             V3Error::pretendError(code, true);
1375         }
1376     });
1377     DECL_OPTION("-Wfuture-", CbPartialMatch, [this](const char* optp) {
1378         // Note it may not be a future option, but one that is currently implemented.
1379         addFuture(optp);
1380     });
1381     DECL_OPTION("-Wno-", CbPartialMatch, [fl, &parser](const char* optp) {
1382         if (!FileLine::globalWarnOff(optp, true)) {
1383             const string fullopt = string{"-Wno-"} + optp;
1384             fl->v3fatal("Unknown warning specified: " << fullopt
1385                                                       << parser.getSuggestion(fullopt.c_str()));
1386         }
1387     });
1388     for (int i = V3ErrorCode::EC_FIRST_WARN; i < V3ErrorCode::_ENUM_MAX; ++i) {
1389         for (const string prefix : {"-Wno-", "-Wwarn-"})
1390             parser.addSuggestionCandidate(prefix + V3ErrorCode{i}.ascii());
1391     }
1392     DECL_OPTION("-Wno-context", CbCall, [this]() { m_context = false; });
1393     DECL_OPTION("-Wno-fatal", CbCall, []() { V3Error::warnFatal(false); });
1394     DECL_OPTION("-Wno-lint", CbCall, []() {
1395         FileLine::globalWarnLintOff(true);
1396         FileLine::globalWarnStyleOff(true);
1397     });
1398     DECL_OPTION("-Wno-style", CbCall, []() { FileLine::globalWarnStyleOff(true); });
1399     DECL_OPTION("-Wwarn-", CbPartialMatch, [this, fl, &parser](const char* optp) {
1400         const V3ErrorCode code{optp};
1401         if (code == V3ErrorCode::EC_ERROR) {
1402             if (!isFuture(optp)) {
1403                 const string fullopt = string{"-Wwarn-"} + optp;
1404                 fl->v3fatal("Unknown warning specified: "
1405                             << fullopt << parser.getSuggestion(fullopt.c_str()));
1406             }
1407         } else {
1408             FileLine::globalWarnOff(code, false);
1409             V3Error::pretendError(code, false);
1410         }
1411     });
1412     DECL_OPTION("-Wwarn-lint", CbCall, []() { FileLine::globalWarnLintOff(false); });
1413     DECL_OPTION("-Wwarn-style", CbCall, []() { FileLine::globalWarnStyleOff(false); });
1414     DECL_OPTION("-waiver-output", Set, &m_waiverOutput);
1415 
1416     DECL_OPTION("-x-assign", CbVal, [this, fl](const char* valp) {
1417         if (!strcmp(valp, "0")) {
1418             m_xAssign = "0";
1419         } else if (!strcmp(valp, "1")) {
1420             m_xAssign = "1";
1421         } else if (!strcmp(valp, "fast")) {
1422             m_xAssign = "fast";
1423         } else if (!strcmp(valp, "unique")) {
1424             m_xAssign = "unique";
1425         } else {
1426             fl->v3fatal("Unknown setting for --x-assign: '"
1427                         << valp << "'\n"
1428                         << fl->warnMore() << "... Suggest '0', '1', 'fast', or 'unique'");
1429         }
1430     });
1431     DECL_OPTION("-x-initial", CbVal, [this, fl](const char* valp) {
1432         if (!strcmp(valp, "0")) {
1433             m_xInitial = "0";
1434         } else if (!strcmp(valp, "fast")) {
1435             m_xInitial = "fast";
1436         } else if (!strcmp(valp, "unique")) {
1437             m_xInitial = "unique";
1438         } else {
1439             fl->v3fatal("Unknown setting for --x-initial: '"
1440                         << valp << "'\n"
1441                         << fl->warnMore() << "... Suggest '0', 'fast', or 'unique'");
1442         }
1443     });
1444     DECL_OPTION("-x-initial-edge", OnOff, &m_xInitialEdge);
1445     DECL_OPTION("-xml-only", OnOff, &m_xmlOnly);
1446     DECL_OPTION("-xml-output", CbVal, [this](const char* valp) {
1447         m_xmlOutput = valp;
1448         m_xmlOnly = true;
1449     });
1450 
1451     DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) {
1452         addIncDirUser(parseFileArg(optdir, string(valp)));
1453     });
1454     parser.finalize();
1455 
1456     for (int i = 0; i < argc;) {
1457         UINFO(9, " Option: " << argv[i] << endl);
1458         if (!strcmp(argv[i], "-j") || !strcmp(argv[i], "--j")) {  // Allow gnu -- switches
1459             ++i;
1460             m_buildJobs = 0;  // Unlimited parallelism
1461             if (i < argc && isdigit(argv[i][0])) {
1462                 m_buildJobs = atoi(argv[i]);
1463                 if (m_buildJobs <= 0) {
1464                     fl->v3error("-j accepts positive integer, but '" << argv[i] << "' is passed");
1465                 }
1466                 ++i;
1467             }
1468         } else if (argv[i][0] == '-' || argv[i][0] == '+') {
1469             if (const int consumed = parser.parse(i, argc, argv)) {
1470                 i += consumed;
1471             } else {
1472                 fl->v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i]));
1473                 ++i;  // LCOV_EXCL_LINE
1474             }
1475         } else {
1476             // Filename
1477             const string filename = parseFileArg(optdir, argv[i]);
1478             if (suffixed(filename, ".cpp")  //
1479                 || suffixed(filename, ".cxx")  //
1480                 || suffixed(filename, ".cc")  //
1481                 || suffixed(filename, ".c")  //
1482                 || suffixed(filename, ".sp")) {
1483                 V3Options::addCppFile(filename);
1484             } else if (suffixed(filename, ".a")  //
1485                        || suffixed(filename, ".o")  //
1486                        || suffixed(filename, ".so")) {
1487                 V3Options::addLdLibs(filename);
1488             } else {
1489                 V3Options::addVFile(filename);
1490             }
1491             ++i;
1492         }
1493     }
1494 }
1495 
1496 //======================================================================
1497 
parseOptsFile(FileLine * fl,const string & filename,bool rel)1498 void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
1499     // Read the specified -f filename and process as arguments
1500     UINFO(1, "Reading Options File " << filename << endl);
1501 
1502     const std::unique_ptr<std::ifstream> ifp{V3File::new_ifstream(filename)};
1503     if (ifp->fail()) {
1504         fl->v3error("Cannot open -f command file: " + filename);
1505         return;
1506     }
1507 
1508     string whole_file;
1509     bool inCmt = false;
1510     while (!ifp->eof()) {
1511         const string line = V3Os::getline(*ifp);
1512         // Strip simple comments
1513         string oline;
1514         // cppcheck-suppress StlMissingComparison
1515         char lastch = ' ';
1516         bool space_begin = true;  // At beginning or leading spaces only
1517         for (string::const_iterator pos = line.begin(); pos != line.end(); lastch = *pos++) {
1518             if (inCmt) {
1519                 if (*pos == '*' && *(pos + 1) == '/') {
1520                     inCmt = false;
1521                     ++pos;
1522                 }
1523             } else if (*pos == '/' && *(pos + 1) == '/'
1524                        && (pos == line.begin() || isspace(lastch))) {  // But allow /file//path
1525                 break;  // Ignore to EOL
1526             } else if (*pos == '#' && space_begin) {  // Only # at [spaced] begin of line
1527                 break;  // Ignore to EOL
1528             } else if (*pos == '/' && *(pos + 1) == '*') {
1529                 inCmt = true;
1530                 space_begin = false;
1531                 // cppcheck-suppress StlMissingComparison
1532                 ++pos;
1533             } else {
1534                 if (!isspace(*pos)) space_begin = false;
1535                 oline += *pos;
1536             }
1537         }
1538         whole_file += oline + " ";
1539     }
1540     whole_file += "\n";  // So string match below is simplified
1541     if (inCmt) fl->v3error("Unterminated /* comment inside -f file.");
1542 
1543     fl = new FileLine(filename);
1544 
1545     // Split into argument list and process
1546     // Note we try to respect escaped char, double/simple quoted strings
1547     // Other simulators don't respect a common syntax...
1548 
1549     // Strip off arguments and parse into words
1550     std::vector<string> args;
1551 
1552     // Parse file using a state machine, taking into account quoted strings and escaped chars
1553     enum state : uint8_t {
1554         ST_IN_OPTION,
1555         ST_ESCAPED_CHAR,
1556         ST_IN_QUOTED_STR,
1557         ST_IN_DOUBLE_QUOTED_STR
1558     };
1559 
1560     state st = ST_IN_OPTION;
1561     state last_st = ST_IN_OPTION;
1562     string arg;
1563     for (string::size_type pos = 0; pos < whole_file.length(); ++pos) {
1564         char curr_char = whole_file[pos];
1565         switch (st) {
1566         case ST_IN_OPTION:  // Get all chars up to a white space or a "="
1567             if (isspace(curr_char)) {  // End of option
1568                 if (!arg.empty()) {  // End of word
1569                     args.push_back(arg);
1570                 }
1571                 arg = "";
1572                 break;
1573             }
1574             if (curr_char == '\\') {  // Escape char, we wait for next char
1575                 last_st = st;  // Memorize current state
1576                 st = ST_ESCAPED_CHAR;
1577                 break;
1578             }
1579             if (curr_char == '\'') {  // Find begin of quoted string
1580                 // Examine next char in order to decide between
1581                 // a string or a base specifier for integer literal
1582                 ++pos;
1583                 if (pos < whole_file.length()) curr_char = whole_file[pos];
1584                 if (curr_char == '"') {  // String
1585                     st = ST_IN_QUOTED_STR;
1586                 } else {  // Base specifier
1587                     arg += '\'';
1588                 }
1589                 arg += curr_char;
1590                 break;
1591             }
1592             if (curr_char == '"') {  // Find begin of double quoted string
1593                 // Doesn't insert the quote
1594                 st = ST_IN_DOUBLE_QUOTED_STR;
1595                 break;
1596             }
1597             arg += curr_char;
1598             break;
1599         case ST_IN_QUOTED_STR:  // Just store all chars inside string
1600             if (curr_char != '\'') {
1601                 arg += curr_char;
1602             } else {  // End of quoted string
1603                 st = ST_IN_OPTION;
1604             }
1605             break;
1606         case ST_IN_DOUBLE_QUOTED_STR:  // Take into account escaped chars
1607             if (curr_char != '"') {
1608                 if (curr_char == '\\') {
1609                     last_st = st;
1610                     st = ST_ESCAPED_CHAR;
1611                 } else {
1612                     arg += curr_char;
1613                 }
1614             } else {  // End of double quoted string
1615                 st = ST_IN_OPTION;
1616             }
1617             break;
1618         case ST_ESCAPED_CHAR:  // Just add the escaped char
1619             arg += curr_char;
1620             st = last_st;
1621             break;
1622         }
1623     }
1624     if (!arg.empty()) {  // Add last word
1625         args.push_back(arg);
1626     }
1627 
1628     // Path
1629     const string optdir = (rel ? V3Os::filenameDir(filename) : ".");
1630 
1631     // Convert to argv style arg list and parse them
1632     std::vector<char*> argv;
1633     argv.reserve(args.size() + 1);
1634     for (const string& i : args) argv.push_back(const_cast<char*>(i.c_str()));
1635     argv.push_back(nullptr);  // argv is nullptr-terminated
1636     parseOptsList(fl, optdir, static_cast<int>(argv.size() - 1), argv.data());
1637 }
1638 
1639 //======================================================================
1640 
parseFileArg(const string & optdir,const string & relfilename)1641 string V3Options::parseFileArg(const string& optdir, const string& relfilename) {
1642     string filename = V3Os::filenameSubstitute(relfilename);
1643     if (optdir != "." && V3Os::filenameIsRel(filename)) filename = optdir + "/" + filename;
1644     return filename;
1645 }
1646 
1647 //======================================================================
1648 
showVersion(bool verbose)1649 void V3Options::showVersion(bool verbose) {
1650     cout << version();
1651     cout << endl;
1652     if (!verbose) return;
1653 
1654     cout << endl;
1655     cout << "Copyright 2003-2021 by Wilson Snyder.  Verilator is free software; you can\n";
1656     cout << "redistribute it and/or modify the Verilator internals under the terms of\n";
1657     cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n";
1658     cout << "License Version 2.0.\n";
1659 
1660     cout << endl;
1661     cout << "See https://verilator.org for documentation\n";
1662 
1663     cout << endl;
1664     cout << "Summary of configuration:\n";
1665     cout << "  Compiled in defaults if not in environment:\n";
1666     cout << "    SYSTEMC            = " << DEFENV_SYSTEMC << endl;
1667     cout << "    SYSTEMC_ARCH       = " << DEFENV_SYSTEMC_ARCH << endl;
1668     cout << "    SYSTEMC_INCLUDE    = " << DEFENV_SYSTEMC_INCLUDE << endl;
1669     cout << "    SYSTEMC_LIBDIR     = " << DEFENV_SYSTEMC_LIBDIR << endl;
1670     cout << "    VERILATOR_ROOT     = " << DEFENV_VERILATOR_ROOT << endl;
1671     cout << "    SystemC system-wide = " << cvtToStr(systemCSystemWide()) << endl;
1672 
1673     cout << endl;
1674     cout << "Environment:\n";
1675     cout << "    MAKE               = " << V3Os::getenvStr("MAKE", "") << endl;
1676     cout << "    PERL               = " << V3Os::getenvStr("PERL", "") << endl;
1677     cout << "    SYSTEMC            = " << V3Os::getenvStr("SYSTEMC", "") << endl;
1678     cout << "    SYSTEMC_ARCH       = " << V3Os::getenvStr("SYSTEMC_ARCH", "") << endl;
1679     cout << "    SYSTEMC_INCLUDE    = " << V3Os::getenvStr("SYSTEMC_INCLUDE", "") << endl;
1680     cout << "    SYSTEMC_LIBDIR     = " << V3Os::getenvStr("SYSTEMC_LIBDIR", "") << endl;
1681     cout << "    VERILATOR_ROOT     = " << V3Os::getenvStr("VERILATOR_ROOT", "") << endl;
1682     // wrapper uses this:
1683     cout << "    VERILATOR_BIN      = " << V3Os::getenvStr("VERILATOR_BIN", "") << endl;
1684 
1685     cout << endl;
1686     cout << "Features (based on environment or compiled-in support):\n";
1687     cout << "    SystemC found      = " << cvtToStr(systemCFound()) << endl;
1688 }
1689 
1690 //======================================================================
1691 
V3Options()1692 V3Options::V3Options() {
1693     m_impp = new V3OptionsImp;
1694 
1695     m_traceFormat = TraceFormat::VCD;
1696 
1697     m_makeDir = "obj_dir";
1698     m_unusedRegexp = "*unused*";
1699     m_xAssign = "fast";
1700 
1701     m_defaultLanguage = V3LangCode::mostRecent();
1702 
1703     VName::maxLength(128);  // Linux filename limits 256; leave half for prefix
1704 
1705     optimize(1);
1706     // Default +libext+
1707     addLibExtV("");  // So include "filename.v" will find the same file
1708     addLibExtV(".v");
1709     addLibExtV(".sv");
1710     // Default -I
1711     addIncDirFallback(".");  // Looks better than {long_cwd_path}/...
1712 }
1713 
~V3Options()1714 V3Options::~V3Options() { VL_DO_CLEAR(delete m_impp, m_impp = nullptr); }
1715 
setDebugMode(int level)1716 void V3Options::setDebugMode(int level) {
1717     V3Error::debugDefault(level);
1718     if (!m_dumpTree) m_dumpTree = 3;  // Don't override if already set.
1719     m_stats = true;
1720     m_debugCheck = true;
1721     cout << "Starting " << version() << endl;
1722 }
1723 
setDebugSrcLevel(const string & srcfile,int level)1724 void V3Options::setDebugSrcLevel(const string& srcfile, int level) {
1725     const auto iter = m_debugSrcs.find(srcfile);
1726     if (iter != m_debugSrcs.end()) {
1727         iter->second = level;
1728     } else {
1729         m_debugSrcs.emplace(srcfile, level);
1730     }
1731 }
1732 
debugSrcLevel(const string & srcfile_path,int default_level)1733 int V3Options::debugSrcLevel(const string& srcfile_path, int default_level) {
1734     // For simplicity, calling functions can just use __FILE__ for srcfile.
1735     // That means though we need to cleanup the filename from ../Foo.cpp -> Foo
1736     const string srcfile = V3Os::filenameNonDirExt(srcfile_path);
1737     const auto iter = m_debugSrcs.find(srcfile);
1738     if (iter != m_debugSrcs.end()) {
1739         return iter->second;
1740     } else {
1741         return default_level;
1742     }
1743 }
1744 
setDumpTreeLevel(const string & srcfile,int level)1745 void V3Options::setDumpTreeLevel(const string& srcfile, int level) {
1746     const auto iter = m_dumpTrees.find(srcfile);
1747     if (iter != m_dumpTrees.end()) {
1748         iter->second = level;
1749     } else {
1750         m_dumpTrees.emplace(srcfile, level);
1751     }
1752 }
1753 
dumpTreeLevel(const string & srcfile_path)1754 int V3Options::dumpTreeLevel(const string& srcfile_path) {
1755     // For simplicity, calling functions can just use __FILE__ for srcfile.
1756     // That means though we need to cleanup the filename from ../Foo.cpp -> Foo
1757     const string srcfile = V3Os::filenameNonDirExt(srcfile_path);
1758     const auto iter = m_dumpTrees.find(srcfile);
1759     if (iter != m_dumpTrees.end()) {
1760         return iter->second;
1761     } else {
1762         return m_dumpTree;
1763     }
1764 }
1765 
optimize(int level)1766 void V3Options::optimize(int level) {
1767     // Set all optimizations to on/off
1768     const bool flag = level > 0;
1769     m_oAcycSimp = flag;
1770     m_oAssemble = flag;
1771     m_oCase = flag;
1772     m_oCombine = flag;
1773     m_oConst = flag;
1774     m_oConstBitOpTree = flag;
1775     m_oDedupe = flag;
1776     m_oExpand = flag;
1777     m_oGate = flag;
1778     m_oInline = flag;
1779     m_oLife = flag;
1780     m_oLifePost = flag;
1781     m_oLocalize = flag;
1782     m_oMergeCond = flag;
1783     m_oReloop = flag;
1784     m_oReorder = flag;
1785     m_oSplit = flag;
1786     m_oSubst = flag;
1787     m_oSubstConst = flag;
1788     m_oTable = flag;
1789     // And set specific optimization levels
1790     if (level >= 3) {
1791         m_inlineMult = -1;  // Maximum inlining
1792     }
1793 }
1794