Internals: Create V3WidthCommit from overly fat header. No functional change intended.

This commit is contained in:
Wilson Snyder 2023-10-06 23:18:26 -04:00
parent 0e0df1fb3b
commit e2343c03e0
8 changed files with 312 additions and 244 deletions

View File

@ -166,6 +166,7 @@ set(HEADERS
V3Waiver.h
V3Width.h
V3WidthCommit.h
V3WidthRemove.h
VlcBucket.h
VlcOptions.h
VlcPoint.h
@ -298,6 +299,7 @@ set(COMMON_SOURCES
V3VariableOrder.cpp
V3Waiver.cpp
V3Width.cpp
V3WidthCommit.cpp
V3WidthSel.cpp
V3ParseImp.cpp
V3ParseGrammar.cpp

View File

@ -278,6 +278,7 @@ RAW_OBJS = \
V3VariableOrder.o \
V3Waiver.o \
V3Width.o \
V3WidthCommit.o \
V3WidthSel.o \
# verilator_coverage
@ -291,7 +292,7 @@ NON_STANDALONE_HEADERS = \
V3AstNodeOther.h \
V3DfgVertices.h \
V3ThreadPool.h \
V3WidthCommit.h \
V3WidthRemove.h \
AST_DEFS := \
V3AstNodeDType.h \

View File

@ -77,12 +77,13 @@
#include "V3Randomize.h"
#include "V3String.h"
#include "V3Task.h"
#include "V3WidthCommit.h"
#include <algorithm>
// More code; this file was getting too large; see actions there
#define VERILATOR_V3WIDTH_CPP_
#include "V3WidthCommit.h"
#include "V3WidthRemove.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@ -4311,8 +4312,7 @@ private:
if (VN_IS(valuep, Const)) {
// Forming a AstConcat will cause problems with
// unsized (uncommitted sized) constants
if (AstConst* const newp
= WidthCommitVisitor::newIfConstCommitSize(VN_AS(valuep, Const))) {
if (AstConst* const newp = V3WidthCommit::newIfConstCommitSize(VN_AS(valuep, Const))) {
VL_DO_DANGLING(pushDeletep(valuep), valuep);
valuep = newp;
}
@ -7614,9 +7614,3 @@ AstNode* V3Width::widthGenerateParamsEdit(
// No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
return nodep;
}
void V3Width::widthCommit(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ WidthCommitVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("widthcommit", 0, dumpTreeLevel() >= 6);
}

View File

@ -33,8 +33,6 @@ public:
static void width(AstNetlist* nodep) VL_MT_DISABLED;
static AstNode* widthParamsEdit(AstNode* nodep) VL_MT_DISABLED;
static AstNode* widthGenerateParamsEdit(AstNode* nodep) VL_MT_DISABLED;
// Final step... Mark all widths as equal
static void widthCommit(AstNetlist* nodep) VL_MT_DISABLED;
static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) VL_MT_DISABLED;

236
src/V3WidthCommit.cpp Normal file
View File

@ -0,0 +1,236 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Commit expression widths
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// Remove all $signed, $unsigned, we're done with them.
//
// This step is only called on real V3Width, not intermediate e.g. widthParams
//
//*************************************************************************
#define VL_MT_DISABLED_CODE_UNIT 1
#include "config_build.h"
#include "verilatedos.h"
#include "V3WidthCommit.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Now that all widthing is complete,
// Copy all width() to widthMin(). V3Const expects this
class WidthCommitVisitor final : public VNVisitor {
// NODE STATE
// AstVar::user1p -> bool, processed
const VNUser1InUse m_inuser1;
// STATE
AstNodeModule* m_modp = nullptr;
public:
// METHODS
VL_DEFINE_DEBUG_FUNCTIONS;
private:
// METHODS
void editDType(AstNode* nodep) {
// Edit dtypes for this node
nodep->dtypep(editOneDType(nodep->dtypep()));
}
AstNodeDType* editOneDType(AstNodeDType* nodep) {
// See if the dtype/refDType can be converted to a standard one
// This reduces the number of dtypes in the system, and since
// dtypep() figures into sameTree() results in better optimizations
if (!nodep) return nullptr;
// Recurse to handle the data type, as may change the size etc of this type
if (!nodep->user1()) iterate(nodep);
// Look for duplicate
if (AstBasicDType* const bdtypep = VN_CAST(nodep, BasicDType)) {
AstBasicDType* const newp = nodep->findInsertSameDType(bdtypep);
if (newp != bdtypep && debug() >= 9) {
UINFO(9, "dtype replacement ");
nodep->dumpSmall(std::cout);
std::cout << " ----> ";
newp->dumpSmall(std::cout);
std::cout << std::endl;
}
return newp;
}
return nodep;
}
void classEncapCheck(AstNode* nodep, AstNode* defp, AstClass* defClassp) {
// Call on non-local class to check local/protected status and complain
bool local = false;
bool prot = false;
if (const auto varp = VN_CAST(defp, Var)) {
local = varp->isHideLocal();
prot = varp->isHideProtected();
} else if (const auto ftaskp = VN_CAST(defp, NodeFTask)) {
local = ftaskp->isHideLocal();
prot = ftaskp->isHideProtected();
} else {
nodep->v3fatalSrc("ref to unhandled definition type " << defp->prettyTypeName());
}
if (local || prot) {
const auto refClassp = VN_CAST(m_modp, Class);
const char* how = nullptr;
if (local && defClassp && refClassp != defClassp) {
how = "'local'";
} else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp)) {
how = "'protected'";
}
if (how) {
UINFO(9, "refclass " << refClassp << endl);
UINFO(9, "defclass " << defClassp << endl);
nodep->v3warn(ENCAPSULATED, nodep->prettyNameQ()
<< " is hidden as " << how
<< " within this context (IEEE 1800-2017 8.18)\n"
<< nodep->warnContextPrimary() << endl
<< nodep->warnOther()
<< "... Location of definition" << endl
<< defp->warnContextSecondary());
}
}
}
// VISITORS
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
iterateChildren(nodep);
editDType(nodep);
}
}
void visit(AstConst* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype");
iterate(nodep->dtypep()); // Do datatype first
if (AstConst* const newp = V3WidthCommit::newIfConstCommitSize(nodep)) {
nodep->replaceWith(newp);
AstNode* const oldp = nodep;
nodep = newp;
// if (debug() > 4) oldp->dumpTree("- fixConstSize_old: ");
// if (debug() > 4) newp->dumpTree("- _new: ");
VL_DO_DANGLING(pushDeletep(oldp), oldp);
}
editDType(nodep);
}
void visit(AstCastWrap* nodep) override {
iterateChildren(nodep);
editDType(nodep);
UINFO(6, " Replace " << nodep << " w/ " << nodep->lhsp() << endl);
nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNodeDType* nodep) override {
// Note some specific dtypes have unique visitors
visitIterateNodeDType(nodep);
}
void visit(AstNodeUOrStructDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
visitIterateNodeDType(nodep);
}
void visit(AstEnumDType* nodep) override {
nodep->tableMap().clear(); // Only needed up through V3Width process
visitIterateNodeDType(nodep);
}
void visit(AstParamTypeDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
visitIterateNodeDType(nodep);
// Move to type table as all dtype pointers must resolve there
nodep->unlinkFrBack(); // Make non-child
v3Global.rootp()->typeTablep()->addTypesp(nodep);
}
void visitIterateNodeDType(AstNodeDType* nodep) {
// Rather than use dtypeChg which may make new nodes, we edit in place,
// as we don't need to preserve any widthMin's, and every dtype with the same width
// gets an identical edit.
if (nodep->user1SetOnce()) return; // Process once
nodep->widthMinFromWidth();
// Too late to any unspecified sign to be anything but unsigned
if (nodep->numeric().isNosign()) nodep->numeric(VSigning::UNSIGNED);
iterateChildren(nodep);
nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep()));
nodep->virtRefDType2p(editOneDType(nodep->virtRefDType2p()));
}
void visit(AstNodeFTask* nodep) override {
iterateChildren(nodep);
editDType(nodep);
AstClass* classp = VN_CAST(m_modp, Class);
if (nodep->classMethod() && nodep->pureVirtual() && classp && !classp->isInterfaceClass()
&& !classp->isVirtual()) {
nodep->v3error(
"Illegal to have 'pure virtual' in non-virtual class (IEEE 1800-2017 8.21)");
}
}
void visit(AstNodeVarRef* nodep) override {
iterateChildren(nodep);
editDType(nodep);
classEncapCheck(nodep, nodep->varp(), VN_CAST(nodep->classOrPackagep(), Class));
}
void visit(AstNodeFTaskRef* nodep) override {
iterateChildren(nodep);
editDType(nodep);
classEncapCheck(nodep, nodep->taskp(), VN_CAST(nodep->classOrPackagep(), Class));
}
void visit(AstMemberSel* nodep) override {
iterateChildren(nodep);
editDType(nodep);
if (auto* const classrefp = VN_CAST(nodep->fromp()->dtypep(), ClassRefDType)) {
classEncapCheck(nodep, nodep->varp(), classrefp->classp());
} // else might be struct, etc
}
void visit(AstVar* nodep) override {
iterateChildren(nodep);
editDType(nodep);
if (nodep->isFuncLocal() && nodep->direction() == VDirection::INPUT && nodep->valuep()) {
// It's the default value of optional argument.
// Such values are added to function calls in V3Width so can be removed now
pushDeletep(nodep->valuep()->unlinkFrBack());
}
}
void visit(AstNodePreSel* nodep) override { // LCOV_EXCL_LINE
// This check could go anywhere after V3Param
nodep->v3fatalSrc("Presels should have been removed before this point");
}
void visit(AstNode* nodep) override {
iterateChildren(nodep);
editDType(nodep);
}
public:
// CONSTRUCTORS
explicit WidthCommitVisitor(AstNetlist* nodep) {
// Were changing widthMin's, so the table is now somewhat trashed
nodep->typeTablep()->clearCache();
iterate(nodep);
// Don't want to AstTypeTable::repairCache, as all needed nodes
// have been added back in; a repair would prevent dead nodes from
// being detected
}
~WidthCommitVisitor() override = default;
};
//######################################################################
// V3WidthCommit class functions
void V3WidthCommit::widthCommit(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ WidthCommitVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("widthcommit", 0, dumpTreeLevel() >= 6);
}

View File

@ -23,60 +23,10 @@
#include "V3Ast.h"
#include "V3Error.h"
// clang-format off
#ifndef VERILATOR_V3WIDTH_CPP_
# error "V3WidthCommit for V3Width internal use only"
#endif
// clang-format on
//######################################################################
/// Remove all $signed, $unsigned, we're done with them.
/// This step is only called on real V3Width, not intermediate e.g. widthParams
class WidthRemoveVisitor final : public VNVisitor {
// METHODS
VL_DEFINE_DEBUG_FUNCTIONS;
void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) {
UINFO(6, " Replace " << nodep << " w/ " << newp << endl);
nodep->replaceWith(newp);
newp->dtypeFrom(nodep);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
// VISITORS
void visit(AstSigned* nodep) override {
VL_DO_DANGLING(replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()), nodep);
}
void visit(AstUnsigned* nodep) override {
VL_DO_DANGLING(replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()), nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
//============================================================================
class V3WidthCommit final {
public:
// CONSTRUCTORS
WidthRemoveVisitor() = default;
~WidthRemoveVisitor() override = default;
AstNode* mainAcceptEdit(AstNode* nodep) { return iterateSubtreeReturnEdits(nodep); }
};
//######################################################################
// Now that all widthing is complete,
// Copy all width() to widthMin(). V3Const expects this
class WidthCommitVisitor final : public VNVisitor {
// NODE STATE
// AstVar::user1p -> bool, processed
const VNUser1InUse m_inuser1;
// STATE
AstNodeModule* m_modp = nullptr;
public:
// METHODS
VL_DEFINE_DEBUG_FUNCTIONS;
static AstConst* newIfConstCommitSize(AstConst* nodep) {
if (((nodep->dtypep()->width() != nodep->num().width()) || !nodep->num().sized())
&& !nodep->num().isString()) { // Need to force the number from unsized to sized
@ -92,185 +42,8 @@ public:
}
}
private:
// METHODS
void editDType(AstNode* nodep) {
// Edit dtypes for this node
nodep->dtypep(editOneDType(nodep->dtypep()));
}
AstNodeDType* editOneDType(AstNodeDType* nodep) {
// See if the dtype/refDType can be converted to a standard one
// This reduces the number of dtypes in the system, and since
// dtypep() figures into sameTree() results in better optimizations
if (!nodep) return nullptr;
// Recurse to handle the data type, as may change the size etc of this type
if (!nodep->user1()) iterate(nodep);
// Look for duplicate
if (AstBasicDType* const bdtypep = VN_CAST(nodep, BasicDType)) {
AstBasicDType* const newp = nodep->findInsertSameDType(bdtypep);
if (newp != bdtypep && debug() >= 9) {
UINFO(9, "dtype replacement ");
nodep->dumpSmall(std::cout);
std::cout << " ----> ";
newp->dumpSmall(std::cout);
std::cout << std::endl;
}
return newp;
}
return nodep;
}
void classEncapCheck(AstNode* nodep, AstNode* defp, AstClass* defClassp) {
// Call on non-local class to check local/protected status and complain
bool local = false;
bool prot = false;
if (const auto varp = VN_CAST(defp, Var)) {
local = varp->isHideLocal();
prot = varp->isHideProtected();
} else if (const auto ftaskp = VN_CAST(defp, NodeFTask)) {
local = ftaskp->isHideLocal();
prot = ftaskp->isHideProtected();
} else {
nodep->v3fatalSrc("ref to unhandled definition type " << defp->prettyTypeName());
}
if (local || prot) {
const auto refClassp = VN_CAST(m_modp, Class);
const char* how = nullptr;
if (local && defClassp && refClassp != defClassp) {
how = "'local'";
} else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp)) {
how = "'protected'";
}
if (how) {
UINFO(9, "refclass " << refClassp << endl);
UINFO(9, "defclass " << defClassp << endl);
nodep->v3warn(ENCAPSULATED, nodep->prettyNameQ()
<< " is hidden as " << how
<< " within this context (IEEE 1800-2017 8.18)\n"
<< nodep->warnContextPrimary() << endl
<< nodep->warnOther()
<< "... Location of definition" << endl
<< defp->warnContextSecondary());
}
}
}
// VISITORS
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
iterateChildren(nodep);
editDType(nodep);
}
}
void visit(AstConst* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype");
iterate(nodep->dtypep()); // Do datatype first
if (AstConst* const newp = newIfConstCommitSize(nodep)) {
nodep->replaceWith(newp);
AstNode* const oldp = nodep;
nodep = newp;
// if (debug() > 4) oldp->dumpTree("- fixConstSize_old: ");
// if (debug() > 4) newp->dumpTree("- _new: ");
VL_DO_DANGLING(pushDeletep(oldp), oldp);
}
editDType(nodep);
}
void visit(AstCastWrap* nodep) override {
iterateChildren(nodep);
editDType(nodep);
UINFO(6, " Replace " << nodep << " w/ " << nodep->lhsp() << endl);
nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNodeDType* nodep) override {
// Note some specific dtypes have unique visitors
visitIterateNodeDType(nodep);
}
void visit(AstNodeUOrStructDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
visitIterateNodeDType(nodep);
}
void visit(AstEnumDType* nodep) override {
nodep->tableMap().clear(); // Only needed up through V3Width process
visitIterateNodeDType(nodep);
}
void visit(AstParamTypeDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
visitIterateNodeDType(nodep);
// Move to type table as all dtype pointers must resolve there
nodep->unlinkFrBack(); // Make non-child
v3Global.rootp()->typeTablep()->addTypesp(nodep);
}
void visitIterateNodeDType(AstNodeDType* nodep) {
// Rather than use dtypeChg which may make new nodes, we edit in place,
// as we don't need to preserve any widthMin's, and every dtype with the same width
// gets an identical edit.
if (nodep->user1SetOnce()) return; // Process once
nodep->widthMinFromWidth();
// Too late to any unspecified sign to be anything but unsigned
if (nodep->numeric().isNosign()) nodep->numeric(VSigning::UNSIGNED);
iterateChildren(nodep);
nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep()));
nodep->virtRefDType2p(editOneDType(nodep->virtRefDType2p()));
}
void visit(AstNodeFTask* nodep) override {
iterateChildren(nodep);
editDType(nodep);
AstClass* classp = VN_CAST(m_modp, Class);
if (nodep->classMethod() && nodep->pureVirtual() && classp && !classp->isInterfaceClass()
&& !classp->isVirtual()) {
nodep->v3error(
"Illegal to have 'pure virtual' in non-virtual class (IEEE 1800-2017 8.21)");
}
}
void visit(AstNodeVarRef* nodep) override {
iterateChildren(nodep);
editDType(nodep);
classEncapCheck(nodep, nodep->varp(), VN_CAST(nodep->classOrPackagep(), Class));
}
void visit(AstNodeFTaskRef* nodep) override {
iterateChildren(nodep);
editDType(nodep);
classEncapCheck(nodep, nodep->taskp(), VN_CAST(nodep->classOrPackagep(), Class));
}
void visit(AstMemberSel* nodep) override {
iterateChildren(nodep);
editDType(nodep);
if (auto* const classrefp = VN_CAST(nodep->fromp()->dtypep(), ClassRefDType)) {
classEncapCheck(nodep, nodep->varp(), classrefp->classp());
} // else might be struct, etc
}
void visit(AstVar* nodep) override {
iterateChildren(nodep);
editDType(nodep);
if (nodep->isFuncLocal() && nodep->direction() == VDirection::INPUT && nodep->valuep()) {
// It's the default value of optional argument.
// Such values are added to function calls in V3Width so can be removed now
pushDeletep(nodep->valuep()->unlinkFrBack());
}
}
void visit(AstNodePreSel* nodep) override { // LCOV_EXCL_LINE
// This check could go anywhere after V3Param
nodep->v3fatalSrc("Presels should have been removed before this point");
}
void visit(AstNode* nodep) override {
iterateChildren(nodep);
editDType(nodep);
}
public:
// CONSTRUCTORS
explicit WidthCommitVisitor(AstNetlist* nodep) {
// Were changing widthMin's, so the table is now somewhat trashed
nodep->typeTablep()->clearCache();
iterate(nodep);
// Don't want to AstTypeTable::repairCache, as all needed nodes
// have been added back in; a repair would prevent dead nodes from
// being detected
}
~WidthCommitVisitor() override = default;
// Final step... Mark all widths as equal
static void widthCommit(AstNetlist* nodep) VL_MT_DISABLED;
};
//######################################################################

63
src/V3WidthRemove.h Normal file
View File

@ -0,0 +1,63 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Cleanup stage in V3Width
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3WIDTHREMOVE_H_
#define VERILATOR_V3WIDTHREMOVE_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Error.h"
// clang-format off
#ifndef VERILATOR_V3WIDTH_CPP_
# error "V3WidthRemove for V3Width internal use only"
#endif
// clang-format on
//######################################################################
class WidthRemoveVisitor final : public VNVisitor {
// METHODS
VL_DEFINE_DEBUG_FUNCTIONS;
void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) {
UINFO(6, " Replace " << nodep << " w/ " << newp << endl);
nodep->replaceWith(newp);
newp->dtypeFrom(nodep);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
// VISITORS
void visit(AstSigned* nodep) override {
VL_DO_DANGLING(replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()), nodep);
}
void visit(AstUnsigned* nodep) override {
VL_DO_DANGLING(replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()), nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
WidthRemoveVisitor() = default;
~WidthRemoveVisitor() override = default;
AstNode* mainAcceptEdit(AstNode* nodep) { return iterateSubtreeReturnEdits(nodep); }
};
//######################################################################
#endif // Guard

View File

@ -104,6 +104,7 @@
#include "V3VariableOrder.h"
#include "V3Waiver.h"
#include "V3Width.h"
#include "V3WidthCommit.h"
#include <ctime>
@ -189,7 +190,7 @@ static void process() {
V3Error::abortIfErrors();
// Commit to the widths we've chosen; Make widthMin==width
V3Width::widthCommit(v3Global.rootp());
V3WidthCommit::widthCommit(v3Global.rootp());
v3Global.assertDTypesResolved(true);
v3Global.widthMinUsage(VWidthMinUsage::MATCHES_WIDTH);