2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2010-01-21 12:11:30 +01:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Configuration Files
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2010-01-21 12:11:30 +01:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2010-2022 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2010-01-21 12:11:30 +01:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2010-01-21 12:11:30 +01:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2010-01-21 12:11:30 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Config.h"
|
|
|
|
|
|
2010-01-21 12:11:30 +01:00
|
|
|
#include "V3Global.h"
|
2012-08-27 03:13:47 +02:00
|
|
|
#include "V3String.h"
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <string>
|
2021-09-27 04:51:11 +02:00
|
|
|
#include <unordered_map>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2010-01-21 12:11:30 +01:00
|
|
|
//######################################################################
|
2020-01-12 10:03:17 +01:00
|
|
|
// Resolve wildcards in files, modules, ftasks or variables
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
// Template for a class that serves as a map for entities that can be specified
|
|
|
|
|
// as wildcards and are accessed by a resolved name. It rebuilds a name lookup
|
|
|
|
|
// cache of resolved entities. Entities stored in this container need an update
|
|
|
|
|
// function that takes a reference of this type to join multiple entities into one.
|
2022-08-05 11:56:57 +02:00
|
|
|
template <typename T>
|
|
|
|
|
class V3ConfigWildcardResolver final {
|
2021-03-13 00:10:45 +01:00
|
|
|
using Map = std::map<const std::string, T>;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
Map m_mapWildcard; // Wildcard strings to entities
|
|
|
|
|
Map m_mapResolved; // Resolved strings to converged entities
|
2010-01-21 12:11:30 +01:00
|
|
|
public:
|
2020-11-17 01:56:16 +01:00
|
|
|
V3ConfigWildcardResolver() = default;
|
|
|
|
|
~V3ConfigWildcardResolver() = default;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
/// Update into maps from other
|
|
|
|
|
void update(const V3ConfigWildcardResolver& other) {
|
2022-05-11 06:47:52 +02:00
|
|
|
for (const auto& itr : other.m_mapResolved) m_mapResolved[itr.first].update(itr.second);
|
|
|
|
|
for (const auto& itr : other.m_mapWildcard) m_mapWildcard[itr.first].update(itr.second);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Access and create a (wildcard) entity
|
|
|
|
|
T& at(const string& name) {
|
|
|
|
|
// Don't store into wildcards if the name is not a wildcard string
|
|
|
|
|
return m_mapWildcard[name];
|
|
|
|
|
}
|
|
|
|
|
// Access an entity and resolve wildcards that match it
|
|
|
|
|
T* resolve(const string& name) {
|
|
|
|
|
// Lookup if it was resolved before, typically not
|
2020-08-16 17:43:49 +02:00
|
|
|
auto it = m_mapResolved.find(name);
|
2021-02-22 03:25:21 +01:00
|
|
|
if (VL_UNLIKELY(it != m_mapResolved.end())) return &it->second;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
2020-08-15 16:12:55 +02:00
|
|
|
T* newp = nullptr;
|
2020-01-12 10:03:17 +01:00
|
|
|
// Cannot be resolved, create if matched
|
|
|
|
|
|
|
|
|
|
// Update this entity with all matches in the wildcards
|
2022-05-11 06:47:52 +02:00
|
|
|
for (const auto& wildent : m_mapWildcard) {
|
|
|
|
|
if (VString::wildmatch(name, wildent.first)) {
|
2020-01-12 10:03:17 +01:00
|
|
|
if (!newp) {
|
|
|
|
|
newp = &m_mapResolved[name]; // Emplace and get pointer
|
|
|
|
|
}
|
2022-05-11 06:47:52 +02:00
|
|
|
newp->update(wildent.second);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return newp;
|
|
|
|
|
}
|
|
|
|
|
// Flush on update
|
|
|
|
|
void flush() { m_mapResolved.clear(); }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Only public_flat_rw has the sensitity tree
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3ConfigVarAttr final {
|
2020-01-12 10:03:17 +01:00
|
|
|
public:
|
2022-01-02 19:56:40 +01:00
|
|
|
VAttrType m_type; // Type of attribute
|
2020-01-12 10:03:17 +01:00
|
|
|
AstSenTree* m_sentreep; // Sensitivity tree for public_flat_rw
|
2021-12-19 20:45:06 +01:00
|
|
|
explicit V3ConfigVarAttr(VAttrType type)
|
|
|
|
|
: m_type{type}
|
|
|
|
|
, m_sentreep{nullptr} {}
|
2022-01-02 19:56:40 +01:00
|
|
|
V3ConfigVarAttr(VAttrType type, AstSenTree* sentreep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_type{type}
|
|
|
|
|
, m_sentreep{sentreep} {}
|
2020-01-12 10:03:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Overload vector with the required update function and to apply all entries
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3ConfigVar final : public std::vector<V3ConfigVarAttr> {
|
2020-01-12 10:03:17 +01:00
|
|
|
public:
|
|
|
|
|
// Update from other by copying all attributes
|
|
|
|
|
void update(const V3ConfigVar& node) {
|
|
|
|
|
reserve(size() + node.size());
|
|
|
|
|
insert(end(), node.begin(), node.end());
|
|
|
|
|
}
|
|
|
|
|
// Apply all attributes to the variable
|
|
|
|
|
void apply(AstVar* varp) {
|
|
|
|
|
for (const_iterator it = begin(); it != end(); ++it) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const newp = new AstAttrOf(varp->fileline(), it->m_type);
|
2020-01-12 10:03:17 +01:00
|
|
|
varp->addAttrsp(newp);
|
2022-01-02 19:56:40 +01:00
|
|
|
if (it->m_type == VAttrType::VAR_PUBLIC_FLAT_RW && it->m_sentreep) {
|
2020-08-15 16:12:55 +02:00
|
|
|
newp->addNext(new AstAlwaysPublic(varp->fileline(), it->m_sentreep, nullptr));
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-01-21 12:11:30 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-13 00:10:45 +01:00
|
|
|
using V3ConfigVarResolver = V3ConfigWildcardResolver<V3ConfigVar>;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Function or task: Have variables and properties
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3ConfigFTask final {
|
2020-01-12 10:03:17 +01:00
|
|
|
V3ConfigVarResolver m_vars; // Variables in function/task
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_isolate = false; // Isolate function return
|
|
|
|
|
bool m_noinline = false; // Don't inline function/task
|
|
|
|
|
bool m_public = false; // Public function/task
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
public:
|
2020-11-17 01:56:16 +01:00
|
|
|
V3ConfigFTask() = default;
|
2020-01-12 10:03:17 +01:00
|
|
|
void update(const V3ConfigFTask& f) {
|
|
|
|
|
// Don't overwrite true with false
|
|
|
|
|
if (f.m_isolate) m_isolate = true;
|
|
|
|
|
if (f.m_noinline) m_noinline = true;
|
|
|
|
|
if (f.m_public) m_public = true;
|
|
|
|
|
m_vars.update(f.m_vars);
|
|
|
|
|
}
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
V3ConfigVarResolver& vars() { return m_vars; }
|
2012-03-20 21:01:53 +01:00
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
void setIsolate(bool set) { m_isolate = set; }
|
|
|
|
|
void setNoInline(bool set) { m_noinline = set; }
|
|
|
|
|
void setPublic(bool set) { m_public = set; }
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2020-08-16 20:55:46 +02:00
|
|
|
void apply(AstNodeFTask* ftaskp) const {
|
2020-01-12 10:03:17 +01:00
|
|
|
if (m_noinline)
|
2022-01-02 19:56:40 +01:00
|
|
|
ftaskp->addStmtsp(new AstPragma(ftaskp->fileline(), VPragmaType::NO_INLINE_TASK));
|
2020-01-12 10:03:17 +01:00
|
|
|
if (m_public)
|
2022-01-02 19:56:40 +01:00
|
|
|
ftaskp->addStmtsp(new AstPragma(ftaskp->fileline(), VPragmaType::PUBLIC_TASK));
|
2020-01-12 10:03:17 +01:00
|
|
|
// Only functions can have isolate (return value)
|
|
|
|
|
if (VN_IS(ftaskp, Func)) ftaskp->attrIsolateAssign(m_isolate);
|
|
|
|
|
}
|
|
|
|
|
};
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2021-03-13 00:10:45 +01:00
|
|
|
using V3ConfigFTaskResolver = V3ConfigWildcardResolver<V3ConfigFTask>;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Modules have tasks, variables, named blocks and properties
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3ConfigModule final {
|
2020-01-12 10:03:17 +01:00
|
|
|
V3ConfigFTaskResolver m_tasks; // Functions/tasks in module
|
|
|
|
|
V3ConfigVarResolver m_vars; // Variables in module
|
2021-03-12 23:26:53 +01:00
|
|
|
std::unordered_set<std::string> m_coverageOffBlocks; // List of block names for coverage_off
|
2022-01-02 19:56:40 +01:00
|
|
|
std::set<VPragmaType> m_modPragmas; // List of Pragmas for modules
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_inline = false; // Whether to force the inline
|
|
|
|
|
bool m_inlineValue = false; // The inline value (on/off)
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
public:
|
2020-11-17 01:56:16 +01:00
|
|
|
V3ConfigModule() = default;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
void update(const V3ConfigModule& m) {
|
|
|
|
|
m_tasks.update(m.m_tasks);
|
|
|
|
|
m_vars.update(m.m_vars);
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const string& i : m.m_coverageOffBlocks) m_coverageOffBlocks.insert(i);
|
2020-01-12 10:03:17 +01:00
|
|
|
if (!m_inline) {
|
|
|
|
|
m_inline = m.m_inline;
|
|
|
|
|
m_inlineValue = m.m_inlineValue;
|
|
|
|
|
}
|
2021-03-12 23:26:53 +01:00
|
|
|
for (auto it = m.m_modPragmas.cbegin(); it != m.m_modPragmas.cend(); ++it) {
|
2020-07-02 13:54:37 +02:00
|
|
|
m_modPragmas.insert(*it);
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
V3ConfigFTaskResolver& ftasks() { return m_tasks; }
|
|
|
|
|
V3ConfigVarResolver& vars() { return m_vars; }
|
|
|
|
|
|
|
|
|
|
void addCoverageBlockOff(const string& name) { m_coverageOffBlocks.insert(name); }
|
|
|
|
|
void setInline(bool set) {
|
|
|
|
|
m_inline = true;
|
|
|
|
|
m_inlineValue = set;
|
|
|
|
|
}
|
2022-01-02 19:56:40 +01:00
|
|
|
void addModulePragma(VPragmaType pragma) { m_modPragmas.insert(pragma); }
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
void apply(AstNodeModule* modp) {
|
|
|
|
|
if (m_inline) {
|
2022-01-02 19:56:40 +01:00
|
|
|
const VPragmaType type
|
|
|
|
|
= m_inlineValue ? VPragmaType::INLINE_MODULE : VPragmaType::NO_INLINE_MODULE;
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const nodep = new AstPragma(modp->fileline(), type);
|
2022-09-15 20:43:56 +02:00
|
|
|
modp->addStmtsp(nodep);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
2022-05-11 06:47:52 +02:00
|
|
|
for (const auto& itr : m_modPragmas) {
|
|
|
|
|
AstNode* const nodep = new AstPragma{modp->fileline(), itr};
|
2022-09-15 20:43:56 +02:00
|
|
|
modp->addStmtsp(nodep);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 03:31:40 +02:00
|
|
|
void applyBlock(AstNodeBlock* nodep) {
|
2022-01-02 19:56:40 +01:00
|
|
|
const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF;
|
2020-01-12 10:03:17 +01:00
|
|
|
if (!nodep->unnamed()) {
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const string& i : m_coverageOffBlocks) {
|
|
|
|
|
if (VString::wildmatch(nodep->name(), i)) {
|
2020-01-12 10:03:17 +01:00
|
|
|
nodep->addStmtsp(new AstPragma(nodep->fileline(), pragma));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-13 00:10:45 +01:00
|
|
|
using V3ConfigModuleResolver = V3ConfigWildcardResolver<V3ConfigModule>;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Files have:
|
|
|
|
|
// - Line ignores (lint/coverage/tracing on/off)
|
|
|
|
|
// - Line attributes: Attributes attached to lines
|
|
|
|
|
|
|
|
|
|
// lint/coverage/tracing on/off
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3ConfigIgnoresLine final {
|
2020-01-12 10:03:17 +01:00
|
|
|
public:
|
2021-11-26 23:55:36 +01:00
|
|
|
const int m_lineno; // Line number to make change at
|
|
|
|
|
const V3ErrorCode m_code; // Error code
|
|
|
|
|
const bool m_on; // True to enable message
|
2020-01-12 10:03:17 +01:00
|
|
|
V3ConfigIgnoresLine(V3ErrorCode code, int lineno, bool on)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_lineno{lineno}
|
|
|
|
|
, m_code{code}
|
|
|
|
|
, m_on{on} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
~V3ConfigIgnoresLine() = default;
|
2020-12-02 00:49:03 +01:00
|
|
|
bool operator<(const V3ConfigIgnoresLine& rh) const {
|
2020-01-12 10:03:17 +01:00
|
|
|
if (m_lineno < rh.m_lineno) return true;
|
|
|
|
|
if (m_lineno > rh.m_lineno) return false;
|
|
|
|
|
if (m_code < rh.m_code) return true;
|
|
|
|
|
if (m_code > rh.m_code) return false;
|
|
|
|
|
// Always turn "on" before "off" so that overlapping lines will end
|
|
|
|
|
// up finally with the error "off"
|
|
|
|
|
return (m_on > rh.m_on);
|
|
|
|
|
}
|
|
|
|
|
};
|
2020-04-16 03:47:37 +02:00
|
|
|
std::ostream& operator<<(std::ostream& os, const V3ConfigIgnoresLine& rhs) {
|
2020-01-12 10:03:17 +01:00
|
|
|
return os << rhs.m_lineno << ", " << rhs.m_code << ", " << rhs.m_on;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Some attributes are attached to entities of the occur on a fileline
|
|
|
|
|
// and multiple attributes can be attached to a line
|
2022-01-02 19:56:40 +01:00
|
|
|
using V3ConfigLineAttribute = std::bitset<VPragmaType::ENUM_SIZE>;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
// File entity
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3ConfigFile final {
|
2021-03-13 00:10:45 +01:00
|
|
|
using LineAttrMap = std::map<int, V3ConfigLineAttribute>; // Map line->bitset of attributes
|
|
|
|
|
using IgnLines = std::multiset<V3ConfigIgnoresLine>; // list of {line,code,on}
|
|
|
|
|
using WaiverSetting = std::pair<V3ErrorCode, std::string>; // Waive code if string matches
|
|
|
|
|
using Waivers = std::vector<WaiverSetting>; // List of {code,wildcard string}
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
LineAttrMap m_lineAttrs; // Atributes to line mapping
|
|
|
|
|
IgnLines m_ignLines; // Ignore line settings
|
2020-04-14 04:51:35 +02:00
|
|
|
Waivers m_waivers; // Waive messages
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
int lineno; // Last line number
|
|
|
|
|
IgnLines::const_iterator it; // Point with next linenumber > current line number
|
|
|
|
|
} m_lastIgnore; // Last ignore line run
|
|
|
|
|
|
|
|
|
|
// Match a given line and attribute to the map, line 0 is any
|
2022-01-02 19:56:40 +01:00
|
|
|
bool lineMatch(int lineno, VPragmaType type) {
|
2020-01-12 10:03:17 +01:00
|
|
|
if (m_lineAttrs.find(0) != m_lineAttrs.end() && m_lineAttrs[0][type]) return true;
|
|
|
|
|
if (m_lineAttrs.find(lineno) == m_lineAttrs.end()) return false;
|
|
|
|
|
return m_lineAttrs[lineno][type];
|
2010-01-21 12:11:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2020-01-22 01:45:44 +01:00
|
|
|
V3ConfigFile() {
|
|
|
|
|
m_lastIgnore.lineno = -1;
|
|
|
|
|
m_lastIgnore.it = m_ignLines.begin();
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
void update(const V3ConfigFile& file) {
|
|
|
|
|
// Copy in all Attributes
|
2020-11-11 04:10:38 +01:00
|
|
|
for (const auto& itr : file.m_lineAttrs) { m_lineAttrs[itr.first] |= itr.second; }
|
2020-01-12 10:03:17 +01:00
|
|
|
// Copy in all ignores
|
2020-11-11 04:10:38 +01:00
|
|
|
for (const auto& ignLine : file.m_ignLines) m_ignLines.insert(ignLine);
|
2020-01-12 10:03:17 +01:00
|
|
|
// Update the iterator after the list has changed
|
|
|
|
|
m_lastIgnore.it = m_ignLines.begin();
|
|
|
|
|
m_waivers.reserve(m_waivers.size() + file.m_waivers.size());
|
|
|
|
|
m_waivers.insert(m_waivers.end(), file.m_waivers.begin(), file.m_waivers.end());
|
|
|
|
|
}
|
2022-01-02 19:56:40 +01:00
|
|
|
void addLineAttribute(int lineno, VPragmaType attr) { m_lineAttrs[lineno].set(attr); }
|
2020-01-12 10:03:17 +01:00
|
|
|
void addIgnore(V3ErrorCode code, int lineno, bool on) {
|
|
|
|
|
m_ignLines.insert(V3ConfigIgnoresLine(code, lineno, on));
|
|
|
|
|
m_lastIgnore.it = m_ignLines.begin();
|
|
|
|
|
}
|
|
|
|
|
void addWaiver(V3ErrorCode code, const string& match) {
|
2021-03-13 00:17:49 +01:00
|
|
|
m_waivers.push_back(std::make_pair(code, match));
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
2010-01-21 12:11:30 +01:00
|
|
|
|
2020-04-23 03:31:40 +02:00
|
|
|
void applyBlock(AstNodeBlock* nodep) {
|
2020-01-12 10:03:17 +01:00
|
|
|
// Apply to block at this line
|
2022-01-02 19:56:40 +01:00
|
|
|
const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF;
|
2020-01-12 10:03:17 +01:00
|
|
|
if (lineMatch(nodep->fileline()->lineno(), pragma)) {
|
|
|
|
|
nodep->addStmtsp(new AstPragma(nodep->fileline(), pragma));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void applyCase(AstCase* nodep) {
|
|
|
|
|
// Apply to this case at this line
|
2021-06-21 00:32:57 +02:00
|
|
|
const int lineno = nodep->fileline()->lineno();
|
2022-01-02 19:56:40 +01:00
|
|
|
if (lineMatch(lineno, VPragmaType::FULL_CASE)) nodep->fullPragma(true);
|
|
|
|
|
if (lineMatch(lineno, VPragmaType::PARALLEL_CASE)) nodep->parallelPragma(true);
|
2010-01-21 12:11:30 +01:00
|
|
|
}
|
2022-09-16 14:17:38 +02:00
|
|
|
void applyIgnores(FileLine* filelinep) {
|
2020-01-12 10:03:17 +01:00
|
|
|
// HOT routine, called each parsed token line of this filename
|
|
|
|
|
if (m_lastIgnore.lineno != filelinep->lineno()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9, " ApplyIgnores for " << filelinep->ascii() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Process all on/offs for lines up to and including the current line
|
2021-06-21 00:32:57 +02:00
|
|
|
const int curlineno = filelinep->lastLineno();
|
2020-01-12 10:03:17 +01:00
|
|
|
for (; m_lastIgnore.it != m_ignLines.end(); ++m_lastIgnore.it) {
|
|
|
|
|
if (m_lastIgnore.it->m_lineno > curlineno) break;
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9, " Hit " << *m_lastIt << endl);
|
2020-01-12 10:03:17 +01:00
|
|
|
filelinep->warnOn(m_lastIgnore.it->m_code, m_lastIgnore.it->m_on);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-06-02 05:16:02 +02:00
|
|
|
if (false && debug() >= 9) {
|
2020-01-12 10:03:17 +01:00
|
|
|
for (IgnLines::const_iterator it = m_lastIgnore.it; it != m_ignLines.end(); ++it) {
|
|
|
|
|
UINFO(9, " NXT " << *it << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
m_lastIgnore.lineno = filelinep->lastLineno();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2010-01-21 12:11:30 +01:00
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
bool waive(V3ErrorCode code, const string& match) {
|
2020-11-11 04:10:38 +01:00
|
|
|
for (const auto& itr : m_waivers) {
|
2022-10-18 01:52:01 +02:00
|
|
|
if (((itr.first == code) || (itr.first == V3ErrorCode::I_LINT)
|
|
|
|
|
|| (code.unusedError() && itr.first == V3ErrorCode::I_UNUSED))
|
|
|
|
|
&& VString::wildmatch(match, itr.second)) {
|
2020-04-14 04:51:35 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-13 00:10:45 +01:00
|
|
|
using V3ConfigFileResolver = V3ConfigWildcardResolver<V3ConfigFile>;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
2022-05-13 04:27:38 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// ScopeTrace tracking
|
|
|
|
|
|
|
|
|
|
class V3ConfigScopeTraceEntry final {
|
|
|
|
|
public:
|
|
|
|
|
const string m_scope; // Scope or regexp to match
|
|
|
|
|
const bool m_on = false; // True to enable message
|
|
|
|
|
int m_levels = 0; // # levels, 0 = all, 1 = only this, ...
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
V3ConfigScopeTraceEntry(const string& scope, bool on, int levels)
|
|
|
|
|
: m_scope{scope}
|
|
|
|
|
, m_on{on}
|
|
|
|
|
, m_levels{levels} {}
|
|
|
|
|
~V3ConfigScopeTraceEntry() = default;
|
|
|
|
|
bool operator<(const V3ConfigScopeTraceEntry& other) const {
|
|
|
|
|
if (m_on < other.m_on) return true;
|
|
|
|
|
if (m_on > other.m_on) return false;
|
|
|
|
|
if (m_levels < other.m_levels) return true;
|
|
|
|
|
if (m_levels > other.m_levels) return false;
|
|
|
|
|
return m_scope < other.m_scope;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Tracks what matches are known to hit against V3ConfigScopeTraceEntries
|
|
|
|
|
class V3ConfigScopeTraceEntryMatch final {
|
|
|
|
|
public:
|
|
|
|
|
const V3ConfigScopeTraceEntry* m_entryp;
|
|
|
|
|
const string m_scopepart;
|
|
|
|
|
V3ConfigScopeTraceEntryMatch(const V3ConfigScopeTraceEntry* entryp, const string& scopepart)
|
|
|
|
|
: m_entryp{entryp}
|
|
|
|
|
, m_scopepart{scopepart} {}
|
|
|
|
|
bool operator<(const V3ConfigScopeTraceEntryMatch& other) const {
|
|
|
|
|
if (m_entryp < other.m_entryp) return true;
|
|
|
|
|
if (m_entryp > other.m_entryp) return false;
|
|
|
|
|
return m_scopepart < other.m_scopepart;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class V3ConfigScopeTraceResolver final {
|
|
|
|
|
std::vector<V3ConfigScopeTraceEntry> m_entries; // User specified on/offs and levels
|
|
|
|
|
std::map<V3ConfigScopeTraceEntryMatch, bool> m_matchCache; // Matching entries for speed
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void addScopeTraceOn(bool on, const string& scope, int levels) {
|
|
|
|
|
UINFO(9, "addScopeTraceOn " << on << " '" << scope << "' "
|
|
|
|
|
<< " levels=" << levels << endl);
|
|
|
|
|
m_entries.emplace_back(V3ConfigScopeTraceEntry{scope, on, levels});
|
|
|
|
|
m_matchCache.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool getEntryMatch(const V3ConfigScopeTraceEntry* entp, const string& scopepart) {
|
|
|
|
|
// Return if a entry matches the scopepart, with memoization
|
|
|
|
|
const auto& key = V3ConfigScopeTraceEntryMatch{entp, scopepart};
|
|
|
|
|
const auto& it = m_matchCache.find(key);
|
|
|
|
|
if (it != m_matchCache.end()) return it->second; // Cached
|
|
|
|
|
const bool matched = VString::wildmatch(scopepart, entp->m_scope);
|
|
|
|
|
m_matchCache.emplace(key, matched);
|
|
|
|
|
return matched;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool getScopeTraceOn(const string& scope) {
|
|
|
|
|
// Apply in the order the user provided them, so they can choose on/off preferencing
|
|
|
|
|
int maxLevel = 1;
|
|
|
|
|
for (const auto& ch : scope) {
|
|
|
|
|
if (ch == '.') ++maxLevel;
|
|
|
|
|
}
|
|
|
|
|
UINFO(9, "getScopeTraceOn " << scope << " maxLevel=" << maxLevel << endl);
|
|
|
|
|
|
|
|
|
|
bool enabled = true;
|
|
|
|
|
for (const auto& ent : m_entries) {
|
|
|
|
|
// We apply shortest match first for each rule component
|
|
|
|
|
// (Otherwise the levels would be useless as "--scope top* --levels 1" would
|
|
|
|
|
// always match at every scopepart, and we wound't know how to count levels)
|
|
|
|
|
int partLevel = 1;
|
|
|
|
|
for (string::size_type partEnd = 0; true;) {
|
|
|
|
|
partEnd = scope.find('.', partEnd + 1);
|
|
|
|
|
if (partEnd == string::npos) partEnd = scope.length();
|
|
|
|
|
const std::string scopepart = scope.substr(0, partEnd);
|
|
|
|
|
if (getEntryMatch(&ent, scopepart)) {
|
|
|
|
|
const bool levelMatch
|
|
|
|
|
= !ent.m_levels || (ent.m_levels >= maxLevel - partLevel);
|
|
|
|
|
if (levelMatch) enabled = ent.m_on;
|
|
|
|
|
UINFO(9, "getScopeTraceOn-part " << scope << " enabled=" << enabled
|
|
|
|
|
<< " @ lev=" << partLevel
|
|
|
|
|
<< (levelMatch ? "[match]" : "[miss]")
|
|
|
|
|
<< " from scopepart=" << scopepart << endl);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (partEnd == scope.length()) break;
|
|
|
|
|
++partLevel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return enabled;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// Resolve modules and files in the design
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3ConfigResolver final {
|
2020-01-12 10:03:17 +01:00
|
|
|
V3ConfigModuleResolver m_modules; // Access to module names (with wildcards)
|
|
|
|
|
V3ConfigFileResolver m_files; // Access to file names (with wildcards)
|
2022-05-13 04:27:38 +02:00
|
|
|
V3ConfigScopeTraceResolver m_scopeTraces; // Regexp to trace enables
|
2022-03-27 21:27:40 +02:00
|
|
|
std::unordered_map<string, std::unordered_map<string, uint64_t>>
|
2021-09-27 04:51:11 +02:00
|
|
|
m_profileData; // Access to profile_data records
|
|
|
|
|
FileLine* m_profileFileLine = nullptr;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
2020-11-17 01:56:16 +01:00
|
|
|
V3ConfigResolver() = default;
|
|
|
|
|
~V3ConfigResolver() = default;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
|
|
|
|
public:
|
2022-05-11 06:47:52 +02:00
|
|
|
static V3ConfigResolver& s() {
|
|
|
|
|
static V3ConfigResolver s_singleton;
|
|
|
|
|
return s_singleton;
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
V3ConfigModuleResolver& modules() { return m_modules; }
|
|
|
|
|
V3ConfigFileResolver& files() { return m_files; }
|
2022-05-13 04:27:38 +02:00
|
|
|
V3ConfigScopeTraceResolver& scopeTraces() { return m_scopeTraces; }
|
2021-09-27 04:51:11 +02:00
|
|
|
|
2022-03-27 21:27:40 +02:00
|
|
|
void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) {
|
2021-09-27 04:51:11 +02:00
|
|
|
if (!m_profileFileLine) m_profileFileLine = fl;
|
|
|
|
|
if (cost == 0) cost = 1; // Cost 0 means delete (or no data)
|
|
|
|
|
m_profileData[model][key] += cost;
|
|
|
|
|
}
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t getProfileData(const string& model, const string& key) const {
|
2021-09-27 04:51:11 +02:00
|
|
|
const auto mit = m_profileData.find(model);
|
|
|
|
|
if (mit == m_profileData.cend()) return 0;
|
|
|
|
|
const auto it = mit->second.find(key);
|
|
|
|
|
if (it == mit->second.cend()) return 0;
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
FileLine* getProfileDataFileLine() const { return m_profileFileLine; } // Maybe null
|
2010-01-21 12:11:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// V3Config
|
|
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
void V3Config::addCaseFull(const string& filename, int lineno) {
|
|
|
|
|
V3ConfigFile& file = V3ConfigResolver::s().files().at(filename);
|
2022-01-02 19:56:40 +01:00
|
|
|
file.addLineAttribute(lineno, VPragmaType::FULL_CASE);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::addCaseParallel(const string& filename, int lineno) {
|
|
|
|
|
V3ConfigFile& file = V3ConfigResolver::s().files().at(filename);
|
2022-01-02 19:56:40 +01:00
|
|
|
file.addLineAttribute(lineno, VPragmaType::PARALLEL_CASE);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::addCoverageBlockOff(const string& filename, int lineno) {
|
|
|
|
|
V3ConfigFile& file = V3ConfigResolver::s().files().at(filename);
|
2022-01-02 19:56:40 +01:00
|
|
|
file.addLineAttribute(lineno, VPragmaType::COVERAGE_BLOCK_OFF);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::addCoverageBlockOff(const string& module, const string& blockname) {
|
|
|
|
|
V3ConfigResolver::s().modules().at(module).addCoverageBlockOff(blockname);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-08 01:31:21 +01:00
|
|
|
void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) {
|
2020-01-12 10:03:17 +01:00
|
|
|
if (filename == "*") {
|
|
|
|
|
FileLine::globalWarnOff(code, !on);
|
|
|
|
|
} else {
|
|
|
|
|
V3ConfigResolver::s().files().at(filename).addIgnore(code, min, on);
|
|
|
|
|
if (max) V3ConfigResolver::s().files().at(filename).addIgnore(code, max, !on);
|
|
|
|
|
V3ConfigResolver::s().files().flush();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::addInline(FileLine* fl, const string& module, const string& ftask, bool on) {
|
|
|
|
|
if (ftask.empty()) {
|
|
|
|
|
V3ConfigResolver::s().modules().at(module).setInline(on);
|
|
|
|
|
} else {
|
|
|
|
|
if (!on) {
|
2020-11-19 03:03:23 +01:00
|
|
|
fl->v3error("no_inline not supported for tasks");
|
2020-01-12 10:03:17 +01:00
|
|
|
} else {
|
|
|
|
|
V3ConfigResolver::s().modules().at(module).ftasks().at(ftask).setNoInline(on);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
void V3Config::addModulePragma(const string& module, VPragmaType pragma) {
|
2021-09-27 04:51:11 +02:00
|
|
|
V3ConfigResolver::s().modules().at(module).addModulePragma(pragma);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::addProfileData(FileLine* fl, const string& model, const string& key,
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t cost) {
|
2021-09-27 04:51:11 +02:00
|
|
|
V3ConfigResolver::s().addProfileData(fl, model, key, cost);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-13 04:27:38 +02:00
|
|
|
void V3Config::addScopeTraceOn(bool on, const string& scope, int levels) {
|
|
|
|
|
V3ConfigResolver::s().scopeTraces().addScopeTraceOn(on, scope, levels);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
void V3Config::addVarAttr(FileLine* fl, const string& module, const string& ftask,
|
2022-01-02 19:56:40 +01:00
|
|
|
const string& var, VAttrType attr, AstSenTree* sensep) {
|
2020-01-12 10:03:17 +01:00
|
|
|
// Semantics: sensep only if public_flat_rw
|
2022-01-02 19:56:40 +01:00
|
|
|
if ((attr != VAttrType::VAR_PUBLIC_FLAT_RW) && sensep) {
|
2020-11-19 03:03:23 +01:00
|
|
|
sensep->v3error("sensitivity not expected for attribute");
|
2020-01-12 10:03:17 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Semantics: Most of the attributes operate on signals
|
|
|
|
|
if (var.empty()) {
|
2022-01-02 19:56:40 +01:00
|
|
|
if (attr == VAttrType::VAR_ISOLATE_ASSIGNMENTS) {
|
2020-01-12 10:03:17 +01:00
|
|
|
if (ftask.empty()) {
|
2020-11-19 03:03:23 +01:00
|
|
|
fl->v3error("isolate_assignments only applies to signals or functions/tasks");
|
2020-01-12 10:03:17 +01:00
|
|
|
} else {
|
|
|
|
|
V3ConfigResolver::s().modules().at(module).ftasks().at(ftask).setIsolate(true);
|
|
|
|
|
}
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (attr == VAttrType::VAR_PUBLIC) {
|
2020-01-12 10:03:17 +01:00
|
|
|
if (ftask.empty()) {
|
|
|
|
|
// public module, this is the only exception from var here
|
2020-07-02 13:54:37 +02:00
|
|
|
V3ConfigResolver::s().modules().at(module).addModulePragma(
|
2022-01-02 19:56:40 +01:00
|
|
|
VPragmaType::PUBLIC_MODULE);
|
2020-01-12 10:03:17 +01:00
|
|
|
} else {
|
|
|
|
|
V3ConfigResolver::s().modules().at(module).ftasks().at(ftask).setPublic(true);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-12-19 20:45:06 +01:00
|
|
|
fl->v3error("missing -var");
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
2010-01-21 12:11:30 +01:00
|
|
|
} else {
|
2021-12-19 20:45:06 +01:00
|
|
|
if (attr == VAttrType::VAR_FORCEABLE) {
|
|
|
|
|
if (module.empty()) {
|
2022-10-22 22:03:42 +02:00
|
|
|
fl->v3error("forceable missing -module");
|
2021-12-19 20:45:06 +01:00
|
|
|
} else if (!ftask.empty()) {
|
|
|
|
|
fl->v3error("Signals inside functions/tasks cannot be marked forceable");
|
|
|
|
|
} else {
|
|
|
|
|
V3ConfigResolver::s().modules().at(module).vars().at(var).push_back(
|
|
|
|
|
V3ConfigVarAttr(attr));
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
} else {
|
2021-12-19 20:45:06 +01:00
|
|
|
V3ConfigModule& mod = V3ConfigResolver::s().modules().at(module);
|
|
|
|
|
if (ftask.empty()) {
|
|
|
|
|
mod.vars().at(var).push_back(V3ConfigVarAttr(attr, sensep));
|
|
|
|
|
} else {
|
|
|
|
|
mod.ftasks().at(ftask).vars().at(var).push_back(V3ConfigVarAttr(attr, sensep));
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
2010-01-21 12:11:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-16 03:47:37 +02:00
|
|
|
void V3Config::addWaiver(V3ErrorCode code, const string& filename, const string& message) {
|
|
|
|
|
V3ConfigResolver::s().files().at(filename).addWaiver(code, message);
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::applyCase(AstCase* nodep) {
|
|
|
|
|
const string& filename = nodep->fileline()->filename();
|
|
|
|
|
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filename);
|
|
|
|
|
if (filep) filep->applyCase(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep) {
|
|
|
|
|
const string& filename = nodep->fileline()->filename();
|
|
|
|
|
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filename);
|
|
|
|
|
if (filep) filep->applyBlock(nodep);
|
|
|
|
|
const string& modname = modulep->name();
|
|
|
|
|
V3ConfigModule* modp = V3ConfigResolver::s().modules().resolve(modname);
|
|
|
|
|
if (modp) modp->applyBlock(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-21 12:11:30 +01:00
|
|
|
void V3Config::applyIgnores(FileLine* filelinep) {
|
2020-01-12 10:03:17 +01:00
|
|
|
const string& filename = filelinep->filename();
|
|
|
|
|
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filename);
|
|
|
|
|
if (filep) filep->applyIgnores(filelinep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::applyModule(AstNodeModule* modulep) {
|
|
|
|
|
const string& modname = modulep->name();
|
|
|
|
|
V3ConfigModule* modp = V3ConfigResolver::s().modules().resolve(modname);
|
|
|
|
|
if (modp) modp->apply(modulep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp) {
|
|
|
|
|
const string& modname = modulep->name();
|
|
|
|
|
V3ConfigModule* modp = V3ConfigResolver::s().modules().resolve(modname);
|
|
|
|
|
if (!modp) return;
|
2021-11-26 23:55:36 +01:00
|
|
|
const V3ConfigFTask* const ftp = modp->ftasks().resolve(ftaskp->name());
|
2020-01-12 10:03:17 +01:00
|
|
|
if (ftp) ftp->apply(ftaskp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp) {
|
|
|
|
|
V3ConfigVar* vp;
|
|
|
|
|
V3ConfigModule* modp = V3ConfigResolver::s().modules().resolve(modulep->name());
|
|
|
|
|
if (!modp) return;
|
|
|
|
|
if (ftaskp) {
|
|
|
|
|
V3ConfigFTask* ftp = modp->ftasks().resolve(ftaskp->name());
|
|
|
|
|
if (!ftp) return;
|
|
|
|
|
vp = ftp->vars().resolve(varp->name());
|
|
|
|
|
} else {
|
|
|
|
|
vp = modp->vars().resolve(varp->name());
|
|
|
|
|
}
|
|
|
|
|
if (vp) vp->apply(varp);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-27 21:27:40 +02:00
|
|
|
uint64_t V3Config::getProfileData(const string& model, const string& key) {
|
2021-09-27 04:51:11 +02:00
|
|
|
return V3ConfigResolver::s().getProfileData(model, key);
|
|
|
|
|
}
|
|
|
|
|
FileLine* V3Config::getProfileDataFileLine() {
|
|
|
|
|
return V3ConfigResolver::s().getProfileDataFileLine();
|
|
|
|
|
}
|
2022-05-13 04:27:38 +02:00
|
|
|
bool V3Config::getScopeTraceOn(const string& scope) {
|
|
|
|
|
return V3ConfigResolver::s().scopeTraces().getScopeTraceOn(scope);
|
|
|
|
|
}
|
2021-09-27 04:51:11 +02:00
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
|
|
|
|
|
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename());
|
|
|
|
|
if (!filep) return false;
|
|
|
|
|
return filep->waive(code, message);
|
2010-01-21 12:11:30 +01:00
|
|
|
}
|