Internals: Optimize temporary memory allocations (#6517)
This patch gets rid of over 80% of temporary dynamic memory allocations (when a malloced node is immediately freed with no other malloc in between). It also gets rid of over 20% of all calls to malloc. It's worth ~3% average verilation speed up with tcmalloc, and more without tcmalloc.
This commit is contained in:
parent
435e1149d5
commit
e9c48cd1ce
98
src/V3Ast.h
98
src/V3Ast.h
|
|
@ -1287,30 +1287,24 @@ void AstNode::foreachImpl(ConstCorrectAstNode<T_Arg>* nodep, const T_Callable& f
|
||||||
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
|
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
|
||||||
using Node = ConstCorrectAstNode<T_Arg>;
|
using Node = ConstCorrectAstNode<T_Arg>;
|
||||||
|
|
||||||
// Traversal stack
|
|
||||||
std::vector<Node*> stack; // Kept as a vector for easy resizing
|
|
||||||
Node** basep = nullptr; // Pointer to base of stack
|
|
||||||
Node** topp = nullptr; // Pointer to top of stack
|
|
||||||
Node** limp = nullptr; // Pointer to stack limit (when need growing)
|
|
||||||
|
|
||||||
// We prefetch this far into the stack
|
// We prefetch this far into the stack
|
||||||
constexpr int prefetchDistance = 2;
|
constexpr int PrefetchDistance = 2;
|
||||||
|
// We push max 5 items per iteration
|
||||||
|
constexpr size_t MaxPushesPerIteration = 5;
|
||||||
|
// Initial stack sie
|
||||||
|
constexpr size_t InitialStackSize = 32;
|
||||||
|
|
||||||
// Grow stack to given size
|
// Traversal stack. Because we often iterate over small trees, use an in-line initial stack
|
||||||
const auto grow = [&](size_t size) {
|
size_t stackSize = InitialStackSize;
|
||||||
const ptrdiff_t occupancy = topp - basep;
|
Node* inLineStack[InitialStackSize]; // In-line initial stack storage
|
||||||
stack.resize(size);
|
std::unique_ptr<Node*[]> outOfLineStackp{nullptr}; // In case we need more, we will allocate
|
||||||
basep = stack.data() + prefetchDistance;
|
Node** basep = inLineStack + PrefetchDistance; // Pointer to base of stack
|
||||||
topp = basep + occupancy;
|
Node** topp = basep; // Pointer to top of stack
|
||||||
limp = basep + size - 5; // We push max 5 items per iteration
|
Node** limp = inLineStack + stackSize - MaxPushesPerIteration; // Pointer to grow limit
|
||||||
};
|
|
||||||
|
|
||||||
// Initial stack size
|
|
||||||
grow(32);
|
|
||||||
|
|
||||||
// We want some non-null pointers at the beginning. These will be prefetched, but not
|
// We want some non-null pointers at the beginning. These will be prefetched, but not
|
||||||
// visited, so the root node will suffice. This eliminates needing branches in the loop.
|
// visited, so the root node will suffice. This eliminates needing branches in the loop.
|
||||||
for (int i = -prefetchDistance; i; ++i) basep[i] = nodep;
|
for (int i = -PrefetchDistance; i; ++i) basep[i] = nodep;
|
||||||
|
|
||||||
// Visit given node, enqueue children for traversal
|
// Visit given node, enqueue children for traversal
|
||||||
const auto visit = [&](Node* currp) {
|
const auto visit = [&](Node* currp) {
|
||||||
|
|
@ -1343,10 +1337,23 @@ void AstNode::foreachImpl(ConstCorrectAstNode<T_Arg>* nodep, const T_Callable& f
|
||||||
Node* const headp = *--topp;
|
Node* const headp = *--topp;
|
||||||
|
|
||||||
// Prefetch in case we are ascending the tree
|
// Prefetch in case we are ascending the tree
|
||||||
ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]);
|
ASTNODE_PREFETCH_NON_NULL(topp[-PrefetchDistance]);
|
||||||
|
|
||||||
// Ensure we have stack space for nextp and the 4 children
|
// Ensure we have stack space for nextp and the 4 children
|
||||||
if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2);
|
if (VL_UNLIKELY(topp >= limp)) {
|
||||||
|
const ptrdiff_t occupancy = topp - basep;
|
||||||
|
{
|
||||||
|
const size_t newStackSize = stackSize * 2;
|
||||||
|
Node** const newStackp = new Node*[newStackSize];
|
||||||
|
std::memcpy(newStackp, basep - PrefetchDistance,
|
||||||
|
sizeof(Node**) * (occupancy + PrefetchDistance));
|
||||||
|
stackSize = newStackSize;
|
||||||
|
outOfLineStackp.reset(newStackp);
|
||||||
|
}
|
||||||
|
basep = outOfLineStackp.get() + PrefetchDistance;
|
||||||
|
topp = basep + occupancy;
|
||||||
|
limp = outOfLineStackp.get() + stackSize - MaxPushesPerIteration;
|
||||||
|
}
|
||||||
|
|
||||||
// Enqueue the next node
|
// Enqueue the next node
|
||||||
if (headp->nextp()) *topp++ = headp->nextp();
|
if (headp->nextp()) *topp++ = headp->nextp();
|
||||||
|
|
@ -1363,30 +1370,24 @@ bool AstNode::predicateImpl(ConstCorrectAstNode<T_Arg>* nodep, const T_Callable&
|
||||||
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
|
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
|
||||||
using Node = ConstCorrectAstNode<T_Arg>;
|
using Node = ConstCorrectAstNode<T_Arg>;
|
||||||
|
|
||||||
// Traversal stack
|
|
||||||
std::vector<Node*> stack; // Kept as a vector for easy resizing
|
|
||||||
Node** basep = nullptr; // Pointer to base of stack
|
|
||||||
Node** topp = nullptr; // Pointer to top of stack
|
|
||||||
Node** limp = nullptr; // Pointer to stack limit (when need growing)
|
|
||||||
|
|
||||||
// We prefetch this far into the stack
|
// We prefetch this far into the stack
|
||||||
constexpr int prefetchDistance = 2;
|
constexpr int PrefetchDistance = 2;
|
||||||
|
// We push max 5 items per iteration
|
||||||
|
constexpr size_t MaxPushesPerIteration = 5;
|
||||||
|
// Initial stack sie
|
||||||
|
constexpr size_t InitialStackSize = 32;
|
||||||
|
|
||||||
// Grow stack to given size
|
// Traversal stack. Because we often iterate over small trees, use an in-line initial stack
|
||||||
const auto grow = [&](size_t size) {
|
size_t stackSize = InitialStackSize;
|
||||||
const ptrdiff_t occupancy = topp - basep;
|
Node* inLineStack[InitialStackSize]; // In-line initial stack storage
|
||||||
stack.resize(size);
|
std::unique_ptr<Node*[]> outOfLineStackp{nullptr}; // In case we need more, we will allocate
|
||||||
basep = stack.data() + prefetchDistance;
|
Node** basep = inLineStack + PrefetchDistance; // Pointer to base of stack
|
||||||
topp = basep + occupancy;
|
Node** topp = basep; // Pointer to top of stack
|
||||||
limp = basep + size - 5; // We push max 5 items per iteration
|
Node** limp = inLineStack + stackSize - MaxPushesPerIteration; // Pointer to grow limit
|
||||||
};
|
|
||||||
|
|
||||||
// Initial stack size
|
|
||||||
grow(32);
|
|
||||||
|
|
||||||
// We want some non-null pointers at the beginning. These will be prefetched, but not
|
// We want some non-null pointers at the beginning. These will be prefetched, but not
|
||||||
// visited, so the root node will suffice. This eliminates needing branches in the loop.
|
// visited, so the root node will suffice. This eliminates needing branches in the loop.
|
||||||
for (int i = -prefetchDistance; i; ++i) basep[i] = nodep;
|
for (int i = -PrefetchDistance; i; ++i) basep[i] = nodep;
|
||||||
|
|
||||||
// Visit given node, enqueue children for traversal, return true if result determined.
|
// Visit given node, enqueue children for traversal, return true if result determined.
|
||||||
const auto visit = [&](Node* currp) {
|
const auto visit = [&](Node* currp) {
|
||||||
|
|
@ -1418,10 +1419,23 @@ bool AstNode::predicateImpl(ConstCorrectAstNode<T_Arg>* nodep, const T_Callable&
|
||||||
Node* const headp = *--topp;
|
Node* const headp = *--topp;
|
||||||
|
|
||||||
// Prefetch in case we are ascending the tree
|
// Prefetch in case we are ascending the tree
|
||||||
ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]);
|
ASTNODE_PREFETCH_NON_NULL(topp[-PrefetchDistance]);
|
||||||
|
|
||||||
// Ensure we have stack space for nextp and the 4 children
|
// Ensure we have stack space for nextp and the 4 children
|
||||||
if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2);
|
if (VL_UNLIKELY(topp >= limp)) {
|
||||||
|
const ptrdiff_t occupancy = topp - basep;
|
||||||
|
{
|
||||||
|
const size_t newStackSize = stackSize * 2;
|
||||||
|
Node** const newStackp = new Node*[newStackSize];
|
||||||
|
std::memcpy(newStackp, basep - PrefetchDistance,
|
||||||
|
sizeof(Node**) * (occupancy + PrefetchDistance));
|
||||||
|
stackSize = newStackSize;
|
||||||
|
outOfLineStackp.reset(newStackp);
|
||||||
|
}
|
||||||
|
basep = outOfLineStackp.get() + PrefetchDistance;
|
||||||
|
topp = basep + occupancy;
|
||||||
|
limp = outOfLineStackp.get() + stackSize - MaxPushesPerIteration;
|
||||||
|
}
|
||||||
|
|
||||||
// Enqueue the next node
|
// Enqueue the next node
|
||||||
if (headp->nextp()) *topp++ = headp->nextp();
|
if (headp->nextp()) *topp++ = headp->nextp();
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,11 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
|
||||||
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
|
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AstVar::sameNode(const AstNode* samep) const {
|
||||||
|
const AstVar* const asamep = VN_DBG_AS(samep, Var);
|
||||||
|
return m_name == asamep->m_name && varType() == asamep->varType();
|
||||||
|
}
|
||||||
|
|
||||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||||
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
|
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
|
||||||
AstVarRef::AstVarRef(FileLine* fl, AstNodeModule* pkgp, AstVar* varp, const VAccess& access)
|
AstVarRef::AstVarRef(FileLine* fl, AstNodeModule* pkgp, AstVar* varp, const VAccess& access)
|
||||||
|
|
@ -190,7 +195,7 @@ bool AstVarRef::sameNode(const AstVarRef* samep) const {
|
||||||
} else {
|
} else {
|
||||||
return (selfPointer() == samep->selfPointer()
|
return (selfPointer() == samep->selfPointer()
|
||||||
&& classOrPackagep() == samep->classOrPackagep() && access() == samep->access()
|
&& classOrPackagep() == samep->classOrPackagep() && access() == samep->access()
|
||||||
&& (varp() && samep->varp() && varp()->name() == samep->varp()->name()));
|
&& (varp() && samep->varp() && varp()->sameNode(samep->varp())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const {
|
bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const {
|
||||||
|
|
@ -200,7 +205,7 @@ bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const {
|
||||||
return (selfPointer() == samep->selfPointer()
|
return (selfPointer() == samep->selfPointer()
|
||||||
&& classOrPackagep() == samep->classOrPackagep()
|
&& classOrPackagep() == samep->classOrPackagep()
|
||||||
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
|
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
|
||||||
&& varp()->name() == samep->varp()->name());
|
&& varp()->sameNode(samep->varp()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1704,6 +1704,7 @@ public:
|
||||||
VSigning numeric);
|
VSigning numeric);
|
||||||
AstBasicDType* findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, const VNumRange& range,
|
AstBasicDType* findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, const VNumRange& range,
|
||||||
int widthMin, VSigning numeric);
|
int widthMin, VSigning numeric);
|
||||||
|
AstBasicDType* findCreateSameDType(AstBasicDType& node);
|
||||||
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||||
AstConstraintRefDType* findConstraintRefDType(FileLine* fl);
|
AstConstraintRefDType* findConstraintRefDType(FileLine* fl);
|
||||||
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
|
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
|
||||||
|
|
@ -2003,7 +2004,7 @@ public:
|
||||||
ASTGEN_MEMBERS_AstVar;
|
ASTGEN_MEMBERS_AstVar;
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
bool sameNode(const AstNode* samep) const override;
|
inline bool sameNode(const AstNode* samep) const override;
|
||||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||||
|
|
|
||||||
|
|
@ -1355,46 +1355,38 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, VBasicDTypeKwd kwd) {
|
AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, VBasicDTypeKwd kwd) {
|
||||||
if (m_basicps[kwd]) return m_basicps[kwd];
|
// Because the detailed map doesn't update m_basicps, check the detailed
|
||||||
//
|
// map for this same node. Also adds this new node to the detailed map
|
||||||
AstBasicDType* const new1p = new AstBasicDType{fl, kwd};
|
if (!m_basicps[kwd]) {
|
||||||
// Because the detailed map doesn't update this map,
|
AstBasicDType basic{fl, kwd};
|
||||||
// check the detailed map for this same node
|
m_basicps[kwd] = findCreateSameDType(basic);
|
||||||
// Also adds this new node to the detailed map
|
|
||||||
AstBasicDType* const newp = findInsertSameDType(new1p);
|
|
||||||
if (newp != new1p) {
|
|
||||||
VL_DO_DANGLING(new1p->deleteTree(), new1p);
|
|
||||||
} else {
|
|
||||||
addTypesp(newp);
|
|
||||||
}
|
}
|
||||||
//
|
return m_basicps[kwd];
|
||||||
m_basicps[kwd] = newp;
|
|
||||||
return newp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, int width,
|
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, int width,
|
||||||
int widthMin, VSigning numeric) {
|
int widthMin, VSigning numeric) {
|
||||||
AstBasicDType* const new1p = new AstBasicDType{fl, kwd, numeric, width, widthMin};
|
AstBasicDType basic{fl, kwd, numeric, width, widthMin};
|
||||||
AstBasicDType* const newp = findInsertSameDType(new1p);
|
return findCreateSameDType(basic);
|
||||||
if (newp != new1p) {
|
|
||||||
VL_DO_DANGLING(new1p->deleteTree(), new1p);
|
|
||||||
} else {
|
|
||||||
addTypesp(newp);
|
|
||||||
}
|
|
||||||
return newp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd,
|
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd,
|
||||||
const VNumRange& range, int widthMin,
|
const VNumRange& range, int widthMin,
|
||||||
VSigning numeric) {
|
VSigning numeric) {
|
||||||
AstBasicDType* const new1p = new AstBasicDType{fl, kwd, numeric, range, widthMin};
|
AstBasicDType basic{fl, kwd, numeric, range, widthMin};
|
||||||
AstBasicDType* const newp = findInsertSameDType(new1p);
|
return findCreateSameDType(basic);
|
||||||
if (newp != new1p) {
|
}
|
||||||
VL_DO_DANGLING(new1p->deleteTree(), new1p);
|
|
||||||
} else {
|
AstBasicDType* AstTypeTable::findCreateSameDType(AstBasicDType& node) {
|
||||||
addTypesp(newp);
|
const VBasicTypeKey key{node.width(), node.widthMin(), node.numeric(), node.keyword(),
|
||||||
|
node.nrange()};
|
||||||
|
AstBasicDType*& entryr = m_detailedMap[key];
|
||||||
|
if (!entryr) {
|
||||||
|
entryr = node.cloneTree(false);
|
||||||
|
entryr->generic(true);
|
||||||
|
addTypesp(entryr);
|
||||||
}
|
}
|
||||||
return newp;
|
return entryr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cppcheck-suppress duplInheritedMember
|
// cppcheck-suppress duplInheritedMember
|
||||||
|
|
@ -2785,10 +2777,6 @@ void AstVar::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonBoolFunc(str, ignoreSchedWrite);
|
dumpJsonBoolFunc(str, ignoreSchedWrite);
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
}
|
}
|
||||||
bool AstVar::sameNode(const AstNode* samep) const {
|
|
||||||
const AstVar* const asamep = VN_DBG_AS(samep, Var);
|
|
||||||
return name() == asamep->name() && varType() == asamep->varType();
|
|
||||||
}
|
|
||||||
void AstScope::dump(std::ostream& str) const {
|
void AstScope::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
str << " [abovep=" << nodeAddr(aboveScopep()) << "]";
|
str << " [abovep=" << nodeAddr(aboveScopep()) << "]";
|
||||||
|
|
|
||||||
|
|
@ -167,55 +167,47 @@ using CacheTernary = Cache<KeyTernary, DfgVertexTernary*>;
|
||||||
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
||||||
inline DfgSel*& getEntry(CacheSel& cache, const DfgDataType& dtype, DfgVertex* src0p,
|
inline DfgSel*& getEntry(CacheSel& cache, const DfgDataType& dtype, DfgVertex* src0p,
|
||||||
uint32_t lsb) {
|
uint32_t lsb) {
|
||||||
return cache
|
const KeySel key{src0p, lsb, dtype.size()};
|
||||||
.emplace(std::piecewise_construct, //
|
return cache[key];
|
||||||
std::forward_as_tuple(src0p, lsb, dtype.size()), //
|
|
||||||
std::forward_as_tuple(nullptr))
|
|
||||||
.first->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DfgVertexUnary*& getEntry(CacheUnary& cache, const DfgDataType&, DfgVertex* src0p) {
|
inline DfgVertexUnary*& getEntry(CacheUnary& cache, const DfgDataType&, DfgVertex* src0p) {
|
||||||
return cache
|
const KeyUnary key{src0p};
|
||||||
.emplace(std::piecewise_construct, //
|
return cache[key];
|
||||||
std::forward_as_tuple(src0p), //
|
|
||||||
std::forward_as_tuple(nullptr))
|
|
||||||
.first->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DfgVertexBinary*& getEntry(CacheBinary& cache, const DfgDataType&, DfgVertex* src0p,
|
inline DfgVertexBinary*& getEntry(CacheBinary& cache, const DfgDataType&, DfgVertex* src0p,
|
||||||
DfgVertex* src1p) {
|
DfgVertex* src1p) {
|
||||||
return cache
|
const KeyBinary key{src0p, src1p};
|
||||||
.emplace(std::piecewise_construct, //
|
return cache[key];
|
||||||
std::forward_as_tuple(src0p, src1p), //
|
|
||||||
std::forward_as_tuple(nullptr))
|
|
||||||
.first->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DfgVertexTernary*& getEntry(CacheTernary& cache, const DfgDataType&, DfgVertex* src0p,
|
inline DfgVertexTernary*& getEntry(CacheTernary& cache, const DfgDataType&, DfgVertex* src0p,
|
||||||
DfgVertex* src1p, DfgVertex* src2p) {
|
DfgVertex* src1p, DfgVertex* src2p) {
|
||||||
return cache
|
const KeyTernary key{src0p, src1p, src2p};
|
||||||
.emplace(std::piecewise_construct, //
|
return cache[key];
|
||||||
std::forward_as_tuple(src0p, src1p, src2p), //
|
|
||||||
std::forward_as_tuple(nullptr))
|
|
||||||
.first->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
||||||
inline CacheSel::iterator find(CacheSel& cache, DfgVertex* src0p, uint32_t lsb, uint32_t size) {
|
inline CacheSel::iterator find(CacheSel& cache, DfgVertex* src0p, uint32_t lsb, uint32_t size) {
|
||||||
return cache.find({src0p, lsb, size});
|
const KeySel key{src0p, lsb, size};
|
||||||
|
return cache.find(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CacheUnary::iterator find(CacheUnary& cache, DfgVertex* src0p) {
|
inline CacheUnary::iterator find(CacheUnary& cache, DfgVertex* src0p) {
|
||||||
return cache.find({src0p});
|
const KeyUnary key{src0p};
|
||||||
|
return cache.find(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CacheBinary::iterator find(CacheBinary& cache, DfgVertex* src0p, DfgVertex* src1p) {
|
inline CacheBinary::iterator find(CacheBinary& cache, DfgVertex* src0p, DfgVertex* src1p) {
|
||||||
return cache.find({src0p, src1p});
|
const KeyBinary key{src0p, src1p};
|
||||||
|
return cache.find(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CacheTernary::iterator find(CacheTernary& cache, DfgVertex* src0p, DfgVertex* src1p,
|
inline CacheTernary::iterator find(CacheTernary& cache, DfgVertex* src0p, DfgVertex* src1p,
|
||||||
DfgVertex* src2p) {
|
DfgVertex* src2p) {
|
||||||
return cache.find({src0p, src1p, src2p});
|
const KeyTernary key{src0p, src1p, src2p};
|
||||||
|
return cache.find(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// These set the operands of a new vertex
|
// These set the operands of a new vertex
|
||||||
|
|
|
||||||
|
|
@ -137,18 +137,18 @@ public:
|
||||||
// Returns a Packed type of the given width
|
// Returns a Packed type of the given width
|
||||||
static const DfgDataType& packed(uint32_t width) {
|
static const DfgDataType& packed(uint32_t width) {
|
||||||
// Find or create the right sized packed type
|
// Find or create the right sized packed type
|
||||||
const auto pair = s_packedTypes.emplace(width, nullptr);
|
const DfgDataType*& entryr = s_packedTypes[width];
|
||||||
if (pair.second) pair.first->second = new DfgDataType{width};
|
if (!entryr) entryr = new DfgDataType{width};
|
||||||
return *pair.first->second;
|
return *entryr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an Array type of the given size with the given elements
|
// Returns an Array type of the given size with the given elements
|
||||||
static const DfgDataType& array(const DfgDataType& elemType, uint32_t size) {
|
static const DfgDataType& array(const DfgDataType& elemType, uint32_t size) {
|
||||||
UASSERT(elemType.isPacked(), "Cannot create multi-dimensional arrays yet");
|
UASSERT(elemType.isPacked(), "Cannot create multi-dimensional arrays yet");
|
||||||
// Find or create the right sized array type with this as elements
|
// Find or create the right sized array type with this as elements
|
||||||
const auto pair = elemType.m_arrayTypes.emplace(size, nullptr);
|
const DfgDataType*& entryr = elemType.m_arrayTypes[size];
|
||||||
if (pair.second) pair.first->second = new DfgDataType{size, elemType};
|
if (!entryr) entryr = new DfgDataType{size, elemType};
|
||||||
return *pair.first->second;
|
return *entryr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the singleton Null type
|
// Returns the singleton Null type
|
||||||
|
|
|
||||||
|
|
@ -651,10 +651,18 @@ V3OutFormatter::V3OutFormatter(V3OutFormatter::Language lang)
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
string V3OutFormatter::indentSpaces(int num) {
|
static constexpr int MAXSPACE = 80; // After this indent, stop indenting more
|
||||||
|
|
||||||
|
// Table of spaces, so we don't have to alloc/free them all the time
|
||||||
|
static const std::array<std::string, MAXSPACE + 1> s_indentSpaces = []() {
|
||||||
|
std::array<std::string, MAXSPACE + 1> table;
|
||||||
|
for (int i = 0; i <= MAXSPACE; ++i) table[i] = std::string(static_cast<size_t>(i), ' ');
|
||||||
|
return table;
|
||||||
|
}();
|
||||||
|
|
||||||
|
const std::string& V3OutFormatter::indentSpaces(int num) {
|
||||||
// Indent the specified number of spaces.
|
// Indent the specified number of spaces.
|
||||||
if (num <= 0) return std::string{};
|
return s_indentSpaces[std::max(0, std::min(num, MAXSPACE))];
|
||||||
return std::string(std::min<size_t>(num, MAXSPACE), ' ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool V3OutFormatter::tokenMatch(const char* cp, const char* cmp) {
|
bool V3OutFormatter::tokenMatch(const char* cp, const char* cmp) {
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,6 @@ public:
|
||||||
|
|
||||||
class V3OutFormatter VL_NOT_FINAL {
|
class V3OutFormatter VL_NOT_FINAL {
|
||||||
// TYPES
|
// TYPES
|
||||||
static constexpr int MAXSPACE = 80; // After this indent, stop indenting more
|
|
||||||
public:
|
public:
|
||||||
enum AlignClass : uint8_t { AL_AUTO = 0, AL_STATIC = 1 };
|
enum AlignClass : uint8_t { AL_AUTO = 0, AL_STATIC = 1 };
|
||||||
enum Language : uint8_t { LA_C, LA_JSON, LA_MK, LA_VERILOG, LA_XML };
|
enum Language : uint8_t { LA_C, LA_JSON, LA_MK, LA_VERILOG, LA_XML };
|
||||||
|
|
@ -173,7 +172,7 @@ public:
|
||||||
if (!m_nobreak) puts("\n");
|
if (!m_nobreak) puts("\n");
|
||||||
}
|
}
|
||||||
// STATIC METHODS
|
// STATIC METHODS
|
||||||
static string indentSpaces(int num);
|
static const std::string& indentSpaces(int num);
|
||||||
// Add escaped characters to strings
|
// Add escaped characters to strings
|
||||||
static string quoteNameControls(const string& namein, Language lang = LA_C) VL_PURE;
|
static string quoteNameControls(const string& namein, Language lang = LA_C) VL_PURE;
|
||||||
static bool tokenMatch(const char* cp, const char* cmp);
|
static bool tokenMatch(const char* cp, const char* cmp);
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,9 @@ class FileLineSingleton final {
|
||||||
~FileLineSingleton() = default;
|
~FileLineSingleton() = default;
|
||||||
|
|
||||||
fileNameIdx_t nameToNumber(const string& filename);
|
fileNameIdx_t nameToNumber(const string& filename);
|
||||||
string numberToName(fileNameIdx_t filenameno) const VL_MT_SAFE { return m_names[filenameno]; }
|
const std::string& numberToName(fileNameIdx_t filenameno) const VL_MT_SAFE {
|
||||||
|
return m_names[filenameno];
|
||||||
|
}
|
||||||
V3LangCode numberToLang(fileNameIdx_t filenameno) const { return m_languages[filenameno]; }
|
V3LangCode numberToLang(fileNameIdx_t filenameno) const { return m_languages[filenameno]; }
|
||||||
void numberToLang(fileNameIdx_t filenameno, const V3LangCode& l) {
|
void numberToLang(fileNameIdx_t filenameno, const V3LangCode& l) {
|
||||||
m_languages[filenameno] = l;
|
m_languages[filenameno] = l;
|
||||||
|
|
@ -291,7 +293,9 @@ public:
|
||||||
string ascii() const VL_MT_SAFE;
|
string ascii() const VL_MT_SAFE;
|
||||||
string asciiLineCol() const;
|
string asciiLineCol() const;
|
||||||
int filenameno() const VL_MT_SAFE { return m_filenameno; }
|
int filenameno() const VL_MT_SAFE { return m_filenameno; }
|
||||||
string filename() const VL_MT_SAFE { return singleton().numberToName(filenameno()); }
|
const std::string& filename() const VL_MT_SAFE {
|
||||||
|
return singleton().numberToName(filenameno());
|
||||||
|
}
|
||||||
// Filename with C string escapes
|
// Filename with C string escapes
|
||||||
string filenameEsc() const VL_MT_SAFE { return VString::quoteBackslash(filename()); }
|
string filenameEsc() const VL_MT_SAFE { return VString::quoteBackslash(filename()); }
|
||||||
bool filenameIsGlobal() const VL_MT_SAFE {
|
bool filenameIsGlobal() const VL_MT_SAFE {
|
||||||
|
|
|
||||||
|
|
@ -465,22 +465,14 @@ void V3Graph::subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp, V3Grap
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Algorithms - sorting
|
// Algorithms - sorting
|
||||||
|
|
||||||
struct GraphSortVertexCmp final {
|
|
||||||
bool operator()(const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) const {
|
|
||||||
return lhsp->sortCmp(rhsp) < 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct GraphSortEdgeCmp final {
|
|
||||||
bool operator()(const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) const {
|
|
||||||
return lhsp->sortCmp(rhsp) < 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void V3Graph::sortVertices() {
|
void V3Graph::sortVertices() {
|
||||||
// Sort list of vertices by rank, then fanout
|
// Sort list of vertices by rank, then fanout
|
||||||
std::vector<V3GraphVertex*> vertexps;
|
std::vector<V3GraphVertex*> vertexps;
|
||||||
for (V3GraphVertex& vertex : m_vertices) vertexps.push_back(&vertex);
|
for (V3GraphVertex& vertex : m_vertices) vertexps.push_back(&vertex);
|
||||||
std::stable_sort(vertexps.begin(), vertexps.end(), GraphSortVertexCmp());
|
std::stable_sort(vertexps.begin(), vertexps.end(),
|
||||||
|
[](const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) { //
|
||||||
|
return lhsp->sortCmp(rhsp) < 0;
|
||||||
|
});
|
||||||
// Re-insert in sorted order
|
// Re-insert in sorted order
|
||||||
for (V3GraphVertex* const vertexp : vertexps) {
|
for (V3GraphVertex* const vertexp : vertexps) {
|
||||||
m_vertices.unlink(vertexp);
|
m_vertices.unlink(vertexp);
|
||||||
|
|
@ -495,7 +487,10 @@ void V3Graph::sortEdges() {
|
||||||
// Make a vector
|
// Make a vector
|
||||||
for (V3GraphEdge& edge : vertex.outEdges()) edges.push_back(&edge);
|
for (V3GraphEdge& edge : vertex.outEdges()) edges.push_back(&edge);
|
||||||
// Sort
|
// Sort
|
||||||
std::stable_sort(edges.begin(), edges.end(), GraphSortEdgeCmp());
|
std::stable_sort(edges.begin(), edges.end(),
|
||||||
|
[](const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) { //
|
||||||
|
return lhsp->sortCmp(rhsp) < 0;
|
||||||
|
});
|
||||||
// Relink edges in specified order
|
// Relink edges in specified order
|
||||||
for (V3GraphEdge* const edgep : edges) edgep->relinkFromp(&vertex);
|
for (V3GraphEdge* const edgep : edges) edgep->relinkFromp(&vertex);
|
||||||
// Prep for next
|
// Prep for next
|
||||||
|
|
|
||||||
|
|
@ -64,12 +64,16 @@ public:
|
||||||
V3Hash operator+(T that) const {
|
V3Hash operator+(T that) const {
|
||||||
return V3Hash{combine(m_value, V3Hash{that}.m_value)};
|
return V3Hash{combine(m_value, V3Hash{that}.m_value)};
|
||||||
}
|
}
|
||||||
|
V3Hash operator+(const std::string& that) const {
|
||||||
|
return V3Hash{combine(m_value, V3Hash{that}.m_value)};
|
||||||
|
}
|
||||||
|
|
||||||
// '+=' combines in place
|
// '+=' combines in place
|
||||||
template <typename T>
|
template <typename T>
|
||||||
V3Hash& operator+=(T that) {
|
V3Hash& operator+=(T that) {
|
||||||
return *this = *this + that;
|
return *this = *this + that;
|
||||||
}
|
}
|
||||||
|
V3Hash& operator+=(const std::string& that) { return *this = *this + that; }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) VL_MT_SAFE;
|
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) VL_MT_SAFE;
|
||||||
|
|
|
||||||
114
src/V3Life.cpp
114
src/V3Life.cpp
|
|
@ -64,55 +64,48 @@ class LifeVarEntry final {
|
||||||
// Last assignment to this varscope, nullptr if no longer relevant
|
// Last assignment to this varscope, nullptr if no longer relevant
|
||||||
AstNodeStmt* m_assignp = nullptr;
|
AstNodeStmt* m_assignp = nullptr;
|
||||||
AstConst* m_constp = nullptr; // Known constant value
|
AstConst* m_constp = nullptr; // Known constant value
|
||||||
|
bool m_isNew = true; // Is just created
|
||||||
// First access was a set (and thus block above may have a set that can be deleted
|
// First access was a set (and thus block above may have a set that can be deleted
|
||||||
bool m_setBeforeUse;
|
bool m_setBeforeUse = false;
|
||||||
// Was ever assigned (and thus above block may not preserve constant propagation)
|
// Was ever assigned (and thus above block may not preserve constant propagation)
|
||||||
bool m_everSet = false;
|
bool m_everSet = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class CRESET {};
|
LifeVarEntry() = default;
|
||||||
class SIMPLEASSIGN {};
|
|
||||||
class COMPLEXASSIGN {};
|
|
||||||
class CONSUMED {};
|
|
||||||
|
|
||||||
LifeVarEntry(CRESET, AstCReset* nodep)
|
|
||||||
: m_setBeforeUse{true} {
|
|
||||||
resetStatement(nodep);
|
|
||||||
}
|
|
||||||
LifeVarEntry(SIMPLEASSIGN, AstNodeAssign* nodep)
|
|
||||||
: m_setBeforeUse{true} {
|
|
||||||
simpleAssign(nodep);
|
|
||||||
}
|
|
||||||
explicit LifeVarEntry(COMPLEXASSIGN)
|
|
||||||
: m_setBeforeUse{false} {
|
|
||||||
complexAssign();
|
|
||||||
}
|
|
||||||
explicit LifeVarEntry(CONSUMED)
|
|
||||||
: m_setBeforeUse{false} {
|
|
||||||
consumed();
|
|
||||||
}
|
|
||||||
~LifeVarEntry() = default;
|
~LifeVarEntry() = default;
|
||||||
|
|
||||||
|
void init(bool setBeforeUse) {
|
||||||
|
UASSERT(m_isNew, "Not a new entry");
|
||||||
|
m_isNew = false;
|
||||||
|
m_setBeforeUse = setBeforeUse;
|
||||||
|
}
|
||||||
|
|
||||||
void simpleAssign(AstNodeAssign* nodep) { // New simple A=.... assignment
|
void simpleAssign(AstNodeAssign* nodep) { // New simple A=.... assignment
|
||||||
|
UASSERT_OBJ(!m_isNew, nodep, "Uninitialzized new entry");
|
||||||
m_assignp = nodep;
|
m_assignp = nodep;
|
||||||
m_constp = nullptr;
|
m_constp = nullptr;
|
||||||
m_everSet = true;
|
m_everSet = true;
|
||||||
if (VN_IS(nodep->rhsp(), Const)) m_constp = VN_AS(nodep->rhsp(), Const);
|
if (VN_IS(nodep->rhsp(), Const)) m_constp = VN_AS(nodep->rhsp(), Const);
|
||||||
}
|
}
|
||||||
void resetStatement(AstCReset* nodep) { // New CReset(A) assignment
|
void resetStatement(AstCReset* nodep) { // New CReset(A) assignment
|
||||||
|
UASSERT_OBJ(!m_isNew, nodep, "Uninitialzized new entry");
|
||||||
m_assignp = nodep;
|
m_assignp = nodep;
|
||||||
m_constp = nullptr;
|
m_constp = nullptr;
|
||||||
m_everSet = true;
|
m_everSet = true;
|
||||||
}
|
}
|
||||||
void complexAssign() { // A[x]=... or some complicated assignment
|
void complexAssign() { // A[x]=... or some complicated assignment
|
||||||
|
UASSERT(!m_isNew, "Uninitialzized new entry");
|
||||||
m_assignp = nullptr;
|
m_assignp = nullptr;
|
||||||
m_constp = nullptr;
|
m_constp = nullptr;
|
||||||
m_everSet = true;
|
m_everSet = true;
|
||||||
}
|
}
|
||||||
void consumed() { // Rvalue read of A
|
void consumed() { // Rvalue read of A
|
||||||
|
UASSERT(!m_isNew, "Uninitialzized new entry");
|
||||||
m_assignp = nullptr;
|
m_assignp = nullptr;
|
||||||
}
|
}
|
||||||
AstNodeStmt* assignp() const { return m_assignp; }
|
AstNodeStmt* assignp() const { return m_assignp; }
|
||||||
AstConst* constNodep() const { return m_constp; }
|
AstConst* constNodep() const { return m_constp; }
|
||||||
|
bool isNew() const { return m_isNew; }
|
||||||
bool setBeforeUse() const { return m_setBeforeUse; }
|
bool setBeforeUse() const { return m_setBeforeUse; }
|
||||||
bool everSet() const { return m_everSet; }
|
bool everSet() const { return m_everSet; }
|
||||||
};
|
};
|
||||||
|
|
@ -126,9 +119,9 @@ class LifeBlock final {
|
||||||
// AstVarScope::user1() -> int. Used in combining to detect duplicates
|
// AstVarScope::user1() -> int. Used in combining to detect duplicates
|
||||||
|
|
||||||
// LIFE MAP
|
// LIFE MAP
|
||||||
// For each basic block, we'll make a new map of what variables that if/else is changing
|
// For each basic block, we'll make a new map of what variables that if/else is changing
|
||||||
using LifeMap = std::unordered_map<AstVarScope*, LifeVarEntry>;
|
// Current active lifetime map for current scope
|
||||||
LifeMap m_map; // Current active lifetime map for current scope
|
std::unordered_map<AstVarScope*, LifeVarEntry> m_map;
|
||||||
LifeBlock* const m_aboveLifep; // Upper life, or nullptr
|
LifeBlock* const m_aboveLifep; // Upper life, or nullptr
|
||||||
LifeState* const m_statep; // Current global state
|
LifeState* const m_statep; // Current global state
|
||||||
bool m_replacedVref = false; // Replaced a variable reference since last clearing
|
bool m_replacedVref = false; // Replaced a variable reference since last clearing
|
||||||
|
|
@ -140,20 +133,19 @@ public:
|
||||||
, m_statep{statep} {}
|
, m_statep{statep} {}
|
||||||
~LifeBlock() = default;
|
~LifeBlock() = default;
|
||||||
// METHODS
|
// METHODS
|
||||||
void checkRemoveAssign(const LifeMap::iterator& it) {
|
void checkRemoveAssign(AstVarScope* vscp, LifeVarEntry& entr) {
|
||||||
const AstVar* const varp = it->first->varp();
|
const AstVar* const varp = vscp->varp();
|
||||||
LifeVarEntry* const entp = &(it->second);
|
|
||||||
if (!varp->isSigPublic() && !varp->sensIfacep()) {
|
if (!varp->isSigPublic() && !varp->sensIfacep()) {
|
||||||
// Rather than track what sigs AstUCFunc/AstUCStmt may change,
|
// Rather than track what sigs AstUCFunc/AstUCStmt may change,
|
||||||
// we just don't optimize any public sigs
|
// we just don't optimize any public sigs
|
||||||
// Check the var entry, and remove if appropriate
|
// Check the var entry, and remove if appropriate
|
||||||
if (AstNodeStmt* const oldassp = entp->assignp()) {
|
if (AstNodeStmt* const oldassp = entr.assignp()) {
|
||||||
UINFO(7, " PREV: " << oldassp);
|
UINFO(7, " PREV: " << oldassp);
|
||||||
// Redundant assignment, in same level block
|
// Redundant assignment, in same level block
|
||||||
// Don't delete it now as it will confuse iteration since it maybe WAY
|
// Don't delete it now as it will confuse iteration since it maybe WAY
|
||||||
// above our current iteration point.
|
// above our current iteration point.
|
||||||
UINFOTREE(7, oldassp, "", "REMOVE/SAMEBLK");
|
UINFOTREE(7, oldassp, "", "REMOVE/SAMEBLK");
|
||||||
entp->complexAssign();
|
entr.complexAssign();
|
||||||
oldassp->unlinkFrBack();
|
oldassp->unlinkFrBack();
|
||||||
if (VN_IS(oldassp, CReset)) {
|
if (VN_IS(oldassp, CReset)) {
|
||||||
++m_statep->m_statCResetDel;
|
++m_statep->m_statCResetDel;
|
||||||
|
|
@ -168,40 +160,43 @@ public:
|
||||||
// Do we have a old assignment we can nuke?
|
// Do we have a old assignment we can nuke?
|
||||||
UINFO(4, " CRESETof: " << nodep);
|
UINFO(4, " CRESETof: " << nodep);
|
||||||
UINFO(7, " new: " << rstp);
|
UINFO(7, " new: " << rstp);
|
||||||
const auto pair = m_map.emplace(std::piecewise_construct, //
|
LifeVarEntry& entr = m_map[nodep];
|
||||||
std::forward_as_tuple(nodep),
|
if (entr.isNew()) {
|
||||||
std::forward_as_tuple(LifeVarEntry::CRESET{}, rstp));
|
entr.init(true);
|
||||||
if (!pair.second) {
|
} else {
|
||||||
checkRemoveAssign(pair.first);
|
checkRemoveAssign(nodep, entr);
|
||||||
pair.first->second.resetStatement(rstp);
|
|
||||||
}
|
}
|
||||||
|
entr.resetStatement(rstp);
|
||||||
// lifeDump();
|
// lifeDump();
|
||||||
}
|
}
|
||||||
void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) {
|
void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) {
|
||||||
// Do we have a old assignment we can nuke?
|
// Do we have a old assignment we can nuke?
|
||||||
UINFO(4, " ASSIGNof: " << nodep);
|
UINFO(4, " ASSIGNof: " << nodep);
|
||||||
UINFO(7, " new: " << assp);
|
UINFO(7, " new: " << assp);
|
||||||
const auto pair = m_map.emplace(std::piecewise_construct, //
|
LifeVarEntry& entr = m_map[nodep];
|
||||||
std::forward_as_tuple(nodep),
|
if (entr.isNew()) {
|
||||||
std::forward_as_tuple(LifeVarEntry::SIMPLEASSIGN{}, assp));
|
entr.init(true);
|
||||||
if (!pair.second) {
|
} else {
|
||||||
checkRemoveAssign(pair.first);
|
checkRemoveAssign(nodep, entr);
|
||||||
pair.first->second.simpleAssign(assp);
|
|
||||||
}
|
}
|
||||||
|
entr.simpleAssign(assp);
|
||||||
// lifeDump();
|
// lifeDump();
|
||||||
}
|
}
|
||||||
void complexAssign(AstVarScope* nodep) {
|
void complexAssign(AstVarScope* nodep) {
|
||||||
UINFO(4, " clearof: " << nodep);
|
UINFO(4, " clearof: " << nodep);
|
||||||
const auto pair = m_map.emplace(nodep, LifeVarEntry::COMPLEXASSIGN{});
|
LifeVarEntry& entr = m_map[nodep];
|
||||||
if (!pair.second) pair.first->second.complexAssign();
|
if (entr.isNew()) entr.init(false);
|
||||||
|
entr.complexAssign();
|
||||||
}
|
}
|
||||||
void clearReplaced() { m_replacedVref = false; }
|
void clearReplaced() { m_replacedVref = false; }
|
||||||
bool replaced() const { return m_replacedVref; }
|
bool replaced() const { return m_replacedVref; }
|
||||||
void varUsageReplace(AstVarScope* nodep, AstVarRef* varrefp) {
|
void varUsageReplace(AstVarScope* nodep, AstVarRef* varrefp) {
|
||||||
// Variable rvalue. If it references a constant, we can replace it
|
// Variable rvalue. If it references a constant, we can replace it
|
||||||
const auto pair = m_map.emplace(nodep, LifeVarEntry::CONSUMED{});
|
LifeVarEntry& entr = m_map[nodep];
|
||||||
if (!pair.second) {
|
if (entr.isNew()) {
|
||||||
if (AstConst* const constp = pair.first->second.constNodep()) {
|
entr.init(false);
|
||||||
|
} else {
|
||||||
|
if (AstConst* const constp = entr.constNodep()) {
|
||||||
if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->sensIfacep()) {
|
if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->sensIfacep()) {
|
||||||
// Aha, variable is constant; substitute in.
|
// Aha, variable is constant; substitute in.
|
||||||
// We'll later constant propagate
|
// We'll later constant propagate
|
||||||
|
|
@ -214,19 +209,19 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UINFO(4, " usage: " << nodep);
|
UINFO(4, " usage: " << nodep);
|
||||||
pair.first->second.consumed();
|
|
||||||
}
|
}
|
||||||
|
entr.consumed();
|
||||||
}
|
}
|
||||||
void complexAssignFind(AstVarScope* nodep) {
|
void complexAssignFind(AstVarScope* nodep) {
|
||||||
const auto pair = m_map.emplace(nodep, LifeVarEntry::COMPLEXASSIGN{});
|
UINFO(4, " casfind: " << nodep);
|
||||||
if (!pair.second) {
|
LifeVarEntry& entr = m_map[nodep];
|
||||||
UINFO(4, " casfind: " << pair.first->first);
|
if (entr.isNew()) entr.init(false);
|
||||||
pair.first->second.complexAssign();
|
entr.complexAssign();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void consumedFind(AstVarScope* nodep) {
|
void consumedFind(AstVarScope* nodep) {
|
||||||
const auto pair = m_map.emplace(nodep, LifeVarEntry::CONSUMED{});
|
LifeVarEntry& entr = m_map[nodep];
|
||||||
if (!pair.second) pair.first->second.consumed();
|
if (entr.isNew()) entr.init(false);
|
||||||
|
entr.consumed();
|
||||||
}
|
}
|
||||||
void lifeToAbove() {
|
void lifeToAbove() {
|
||||||
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
||||||
|
|
@ -259,7 +254,7 @@ public:
|
||||||
// Both branches set the var, we can remove the assignment before the IF.
|
// Both branches set the var, we can remove the assignment before the IF.
|
||||||
UINFO(4, "DUALBRANCH " << nodep);
|
UINFO(4, "DUALBRANCH " << nodep);
|
||||||
const auto itab = m_map.find(nodep);
|
const auto itab = m_map.find(nodep);
|
||||||
if (itab != m_map.end()) checkRemoveAssign(itab);
|
if (itab != m_map.end()) checkRemoveAssign(nodep, itab->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this->lifeDump();
|
// this->lifeDump();
|
||||||
|
|
@ -287,12 +282,7 @@ class LifeVisitor final : public VNVisitor {
|
||||||
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
||||||
bool m_noopt = false; // Disable optimization of variables in this block
|
bool m_noopt = false; // Disable optimization of variables in this block
|
||||||
bool m_tracingCall = false; // Iterating into a CCall to a CFunc
|
bool m_tracingCall = false; // Iterating into a CCall to a CFunc
|
||||||
|
LifeBlock* m_lifep = nullptr; // Current active lifetime map for current scope
|
||||||
// LIFE MAP
|
|
||||||
// For each basic block, we'll make a new map of what variables that if/else is changing
|
|
||||||
using LifeMap = std::unordered_map<AstVarScope*, LifeVarEntry>;
|
|
||||||
// cppcheck-suppress memleak // cppcheck bug - it is deleted
|
|
||||||
LifeBlock* m_lifep; // Current active lifetime map for current scope
|
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void setNoopt() {
|
void setNoopt() {
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ class LocalizeVisitor final : public VNVisitor {
|
||||||
AstVarScope* const varScopep = nodep->varScopep();
|
AstVarScope* const varScopep = nodep->varScopep();
|
||||||
// Remember this function accesses this VarScope (we always need this as we might optimize
|
// Remember this function accesses this VarScope (we always need this as we might optimize
|
||||||
// this VarScope into a local, even if it's not assigned. See 'isOptimizable')
|
// this VarScope into a local, even if it's not assigned. See 'isOptimizable')
|
||||||
m_accessors(varScopep).emplace(m_cfuncp);
|
m_accessors(varScopep).insert(m_cfuncp); // emplace performs a temporary malloc
|
||||||
// Remember the reference so we can fix it up later (we always need this as well)
|
// Remember the reference so we can fix it up later (we always need this as well)
|
||||||
m_references(m_cfuncp).emplace(varScopep, nodep);
|
m_references(m_cfuncp).emplace(varScopep, nodep);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,8 @@ public:
|
||||||
// For getline()
|
// For getline()
|
||||||
string m_lineChars; ///< Characters left for next line
|
string m_lineChars; ///< Characters left for next line
|
||||||
|
|
||||||
|
string m_tokenBuf; // Token buffer, to avoid repeated alloc/free
|
||||||
|
|
||||||
void v3errorEnd(std::ostringstream& str) VL_RELEASE(V3Error::s().m_mutex) {
|
void v3errorEnd(std::ostringstream& str) VL_RELEASE(V3Error::s().m_mutex) {
|
||||||
fileline()->v3errorEnd(str);
|
fileline()->v3errorEnd(str);
|
||||||
}
|
}
|
||||||
|
|
@ -1687,7 +1689,7 @@ int V3PreProcImp::getFinalToken(string& buf) {
|
||||||
if (!m_finAhead) {
|
if (!m_finAhead) {
|
||||||
m_finAhead = true;
|
m_finAhead = true;
|
||||||
m_finToken = getStateToken();
|
m_finToken = getStateToken();
|
||||||
m_finBuf = string{yyourtext(), yyourleng()};
|
m_finBuf.assign(yyourtext(), yyourleng());
|
||||||
}
|
}
|
||||||
const int tok = m_finToken;
|
const int tok = m_finToken;
|
||||||
buf = m_finBuf;
|
buf = m_finBuf;
|
||||||
|
|
@ -1748,10 +1750,10 @@ string V3PreProcImp::getline() {
|
||||||
const char* rtnp;
|
const char* rtnp;
|
||||||
bool gotEof = false;
|
bool gotEof = false;
|
||||||
while (nullptr == (rtnp = std::strchr(m_lineChars.c_str(), '\n')) && !gotEof) {
|
while (nullptr == (rtnp = std::strchr(m_lineChars.c_str(), '\n')) && !gotEof) {
|
||||||
string buf;
|
m_tokenBuf.clear();
|
||||||
const int tok = getFinalToken(buf /*ref*/);
|
const int tok = getFinalToken(m_tokenBuf /*ref*/);
|
||||||
if (debug() >= 5) {
|
if (debug() >= 5) {
|
||||||
const string bufcln = V3PreLex::cleanDbgStrg(buf);
|
const string bufcln = V3PreLex::cleanDbgStrg(m_tokenBuf);
|
||||||
const string flcol = m_lexp->m_tokFilelinep->asciiLineCol();
|
const string flcol = m_lexp->m_tokFilelinep->asciiLineCol();
|
||||||
UINFO(0, flcol << ": GETFETC: " << tokenName(tok) << ": " << bufcln);
|
UINFO(0, flcol << ": GETFETC: " << tokenName(tok) << ": " << bufcln);
|
||||||
}
|
}
|
||||||
|
|
@ -1762,7 +1764,7 @@ string V3PreProcImp::getline() {
|
||||||
}
|
}
|
||||||
gotEof = true;
|
gotEof = true;
|
||||||
} else {
|
} else {
|
||||||
m_lineChars.append(buf);
|
m_lineChars.append(m_tokenBuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -169,11 +169,6 @@ public:
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Top Stats class
|
// Top Stats class
|
||||||
|
|
||||||
void V3Stats::addStatSum(const string& name, double count) VL_MT_SAFE_EXCLUDES(s_mutex) {
|
|
||||||
V3LockGuard lock{s_mutex};
|
|
||||||
addStat(V3Statistic{"*", name, count, 0, true});
|
|
||||||
}
|
|
||||||
|
|
||||||
void V3Stats::statsStageAll(AstNetlist* nodep, const std::string& stage, bool fastOnly) {
|
void V3Stats::statsStageAll(AstNetlist* nodep, const std::string& stage, bool fastOnly) {
|
||||||
StatsVisitor{nodep, stage, fastOnly};
|
StatsVisitor{nodep, stage, fastOnly};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ public:
|
||||||
}
|
}
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
V3Statistic(const string& stage, const string& name, double value, unsigned precision,
|
V3Statistic(const string& stage, const string& name, double value, unsigned precision,
|
||||||
bool sumit = false, bool perf = false)
|
bool sumit, bool perf)
|
||||||
: m_name{name}
|
: m_name{name}
|
||||||
, m_value{value}
|
, m_value{value}
|
||||||
, m_precision{precision}
|
, m_precision{precision}
|
||||||
|
|
@ -121,13 +121,22 @@ public:
|
||||||
static void addStat(const V3Statistic&);
|
static void addStat(const V3Statistic&);
|
||||||
static void addStat(const string& stage, const string& name, double value,
|
static void addStat(const string& stage, const string& name, double value,
|
||||||
unsigned precision = 0) {
|
unsigned precision = 0) {
|
||||||
addStat(V3Statistic{stage, name, value, precision});
|
addStat(V3Statistic{stage, name, value, precision, false, false});
|
||||||
}
|
}
|
||||||
static void addStat(const string& name, double value, unsigned precision = 0) {
|
static void addStat(const string& name, double value, unsigned precision = 0) {
|
||||||
addStat(V3Statistic{"*", name, value, precision});
|
addStat(V3Statistic{"*", name, value, precision, false, false});
|
||||||
}
|
}
|
||||||
// Add summary statistic - Threadsafe _unlike most other functions here_
|
// Add summary statistic - Threadsafe _unlike most other functions here_
|
||||||
static void addStatSum(const string& name, double count) VL_MT_SAFE_EXCLUDES(s_mutex);
|
static void addStatSum(const char* name, double count) VL_MT_SAFE_EXCLUDES(s_mutex) {
|
||||||
|
// Avoid memory blow-up when called frequently with zero adds,
|
||||||
|
// e.g. from V3Const invoked on individual expressions.
|
||||||
|
if (count == 0.0) return;
|
||||||
|
V3LockGuard lock{s_mutex};
|
||||||
|
addStat(V3Statistic{"*", name, count, 0, true, false});
|
||||||
|
}
|
||||||
|
static void addStatSum(const std::string& name, double count) {
|
||||||
|
addStatSum(name.c_str(), count);
|
||||||
|
}
|
||||||
static void addStatPerf(const string& name, double value) {
|
static void addStatPerf(const string& name, double value) {
|
||||||
addStat(V3Statistic{"*", name, value, 6, true, true});
|
addStat(V3Statistic{"*", name, value, 6, true, true});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,12 +160,7 @@ class StatsReport final {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// METHODS
|
// METHODS
|
||||||
static void addStat(const V3Statistic& stat) {
|
static void addStat(const V3Statistic& stat) { s_allStats.push_back(stat); }
|
||||||
// Avoid memory blow-up when called frequently with zero adds,
|
|
||||||
// e.g. from V3Const invoked on individual expressions.
|
|
||||||
if (stat.sumit() && stat.value() == 0.0) return;
|
|
||||||
s_allStats.push_back(stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double getStatSum(const string& name) {
|
static double getStatSum(const string& name) {
|
||||||
// O(n^2) if called a lot; present assumption is only a small call count
|
// O(n^2) if called a lot; present assumption is only a small call count
|
||||||
|
|
|
||||||
|
|
@ -327,10 +327,6 @@ string VString::replaceWord(const string& str, const string& from, const string&
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VString::startsWith(const string& str, const string& prefix) {
|
|
||||||
return str.rfind(prefix, 0) == 0; // Faster than .find(_) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VString::endsWith(const string& str, const string& suffix) {
|
bool VString::endsWith(const string& str, const string& suffix) {
|
||||||
if (str.length() < suffix.length()) return false;
|
if (str.length() < suffix.length()) return false;
|
||||||
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
|
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,12 @@ public:
|
||||||
// e.g.: replaceWords("one apple bad_apple", "apple", "banana") -> "one banana bad_apple"
|
// e.g.: replaceWords("one apple bad_apple", "apple", "banana") -> "one banana bad_apple"
|
||||||
static string replaceWord(const string& str, const string& from, const string& to);
|
static string replaceWord(const string& str, const string& from, const string& to);
|
||||||
// Predicate to check if 'str' starts with 'prefix'
|
// Predicate to check if 'str' starts with 'prefix'
|
||||||
static bool startsWith(const string& str, const string& prefix);
|
static bool startsWith(const string& str, const char* prefixp) {
|
||||||
|
return !str.rfind(prefixp, 0); // Faster than .find(_) == 0
|
||||||
|
}
|
||||||
|
static bool startsWith(const string& str, const string& prefix) {
|
||||||
|
return !str.rfind(prefix, 0); // Faster than .find(_) == 0
|
||||||
|
}
|
||||||
// Predicate to check if 'str' ends with 'suffix'
|
// Predicate to check if 'str' ends with 'suffix'
|
||||||
static bool endsWith(const string& str, const string& suffix);
|
static bool endsWith(const string& str, const string& suffix);
|
||||||
// Return proper article (a/an) for a word. May be inaccurate for some special words
|
// Return proper article (a/an) for a word. May be inaccurate for some special words
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue