From 87e87368236049ba2ba3b36f003c6e3e02231078 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 29 Apr 2012 10:14:13 -0400 Subject: [PATCH] IMPORTANT: Major internal changes for supporting complex data types. Adds dtype() to every node, keep global table of dtypes and remove duplicates. Final merge from dtype branch. --- Changes | 2 + internals.pod | 22 +- src/V3Ast.cpp | 65 +++++- src/V3Ast.h | 219 ++++++++++++++---- src/V3AstNodes.cpp | 149 +++++++++++- src/V3AstNodes.h | 386 +++++++++++++++++++++---------- src/V3Broken.cpp | 17 +- src/V3Clean.cpp | 26 ++- src/V3Const.cpp | 30 +-- src/V3Dead.cpp | 34 ++- src/V3EmitV.cpp | 6 + src/V3Expand.cpp | 14 +- src/V3Hashed.cpp | 3 +- src/V3Inst.cpp | 14 +- src/V3Link.cpp | 1 - src/V3LinkJump.cpp | 4 +- src/V3LinkLValue.cpp | 11 - src/V3LinkLevel.cpp | 1 - src/V3LinkParse.cpp | 2 +- src/V3LinkResolve.cpp | 7 - src/V3Param.cpp | 2 +- src/V3Premit.cpp | 7 +- src/V3Slice.cpp | 3 + src/V3Stats.cpp | 2 +- src/V3Table.cpp | 13 +- src/V3Task.cpp | 2 +- src/V3Tristate.cpp | 10 +- src/V3Unknown.cpp | 2 +- src/V3Width.cpp | 298 ++++++++++++++++-------- src/V3WidthCommit.h | 69 +++++- src/V3WidthSel.cpp | 13 +- src/Verilator.cpp | 1 + src/verilog.y | 12 +- test_regress/t/t_cover_toggle.pl | 2 +- 34 files changed, 1049 insertions(+), 400 deletions(-) diff --git a/Changes b/Changes index b7655512b..83aac589f 100644 --- a/Changes +++ b/Changes @@ -9,6 +9,8 @@ indicates the contributor was also the author of the fix; Thanks! concatenates and pullup/pulldowns, bug395, bug56, bug54, bug51. [Alex Solomatnikov, Lane Brooks, et al] +** Major internal changes to support future complex data types. + *** Support tri0 and tri1, bug462. [Alex Solomatnikov] *** Support nmos and pmos, bug488. [Alex Solomatnikov] diff --git a/internals.pod b/internals.pod index 0a51b5aa8..ef0cb6887 100644 --- a/internals.pod +++ b/internals.pod @@ -369,10 +369,10 @@ viewers let us know; ZGRViewer isn't great for large graphs. Tree files are dumps of the AST Tree and are produced between every major algorithmic stage. An example: - NETLIST 0x90fb00 {a0} w0 - 1: MODULE 0x912b20 {a8} w0 top L2 [P] - *1:2: VAR 0x91a780 {a22} w70 out_wide [O] WIRE - 1:2:1: BASICDTYPE 0x91a3c0 {a22} w70 [logic] + NETLIST 0x90fb00 {a0} + 1: MODULE 0x912b20 {a8} top L2 [P] + *1:2: VAR 0x91a780 {a22} @dt=0xa2e640(w32) out_wide [O] WIRE + 1:2:1: BASICDTYPE 0xa2e640 {e24} @dt=this(sw32) integer kwd=integer range=[31:0] =over 4 @@ -389,7 +389,9 @@ this node. "{a22}" indicates this node is related to line 22 in the source filename "a", where "a" is the first file read, "z" the 26th, and "aa" the 27th. -"w70" indicates the width is 70 bits. +"@dt=0x..." indicates the address of the data type this node contains. + +"w32" indicates the width is 32 bits. "out_wide" is the name of the node, in this case the name of the variable. @@ -449,11 +451,13 @@ C, the 26th is C, the 27th is C and so on. Shows the value of the node's user1p...user5p, if non-NULL. -=item Width of the item +=item Data type -Many nodes have an explicit size, other "unsized" nodes have a minimum -width for their implementation. This field is a squence of flag characters -and width data as follows: +Many nodes have an explicit data type. "@dt=0x..." indicates the address +of the data type (AstNodeDType) this node uses. + +If a data type is present and is numberic, it then prints the width of the +item. This field is a squence of flag characters and width data as follows: C if the node is signed. diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 944228a7d..f223e58c2 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -53,6 +53,9 @@ bool AstUser3InUse::s_userBusy=false; bool AstUser4InUse::s_userBusy=false; bool AstUser5InUse::s_userBusy=false; +int AstNodeDType::s_uniqueNum = 0; + + //###################################################################### // V3AstType @@ -72,14 +75,12 @@ void AstNode::init() { m_op3p = NULL; m_op4p = NULL; m_iterpp = NULL; + m_dtypep = NULL; m_clonep = NULL; m_cloneCnt = 0; // Attributes - m_numeric = (int)AstNumeric::UNSIGNED; m_didWidth = false; m_doingWidth = false; - m_width = 0; - m_widthMin = 0; m_user1p = NULL; m_user1Cnt = 0; m_user2p = NULL; @@ -856,6 +857,9 @@ AstNode* AstNode::acceptSubtreeReturnEdits(AstNVisitor& v, AstNUser* vup) { void AstNode::cloneRelinkTree() { if (!this) return; for (AstNode* nodep=this; nodep; nodep=nodep->m_nextp) { + if (m_dtypep && m_dtypep->clonep()) { + m_dtypep = m_dtypep->clonep()->castNodeDType(); + } nodep->cloneRelink(); nodep->m_op1p->cloneRelinkTree(); nodep->m_op2p->cloneRelinkTree(); @@ -876,8 +880,7 @@ bool AstNode::sameTreeIter(AstNode* node2p, bool ignNext) { if (this==NULL && node2p==NULL) return true; if (this==NULL || node2p==NULL) return false; if (this->type() != node2p->type() - || this->width() != node2p->width() - || this->numeric() != node2p->numeric() + || this->dtypep() != node2p->dtypep() || !this->same(node2p)) { return false; } @@ -1014,7 +1017,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append) { UINFO(2,"Dumping "< logsp (V3File::new_ofstream(filename, append)); if (logsp->fail()) v3fatalSrc("Can't write "<"; + *logsp<<"Verilator Tree Dump (format 0x3900) from "; *logsp<<" to "<=9)) { @@ -1050,6 +1053,56 @@ void AstNode::v3errorEnd(ostringstream& str) const { } } +//====================================================================== +// Data type conversion + +void AstNode::dtypeChgSigned(bool flag) { + if (!dtypep()) this->v3fatalSrc("No dtype when changing to (un)signed"); + dtypeChgWidthSigned(dtypep()->width(), dtypep()->widthMin(), + flag ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); +} +void AstNode::dtypeChgWidth(int width, int widthMin) { + if (!dtypep()) this->v3fatalSrc("No dtype when changing width"); // Use ChgWidthSigned(...UNSIGNED) otherwise + dtypeChgWidthSigned(width, widthMin, dtypep()->numeric()); +} + +void AstNode::dtypeChgWidthSigned(int width, int widthMin, bool issigned) { + AstNumeric numeric = issigned ? AstNumeric::SIGNED : AstNumeric::UNSIGNED; + if (!dtypep()) { + // We allow dtypep() to be null, as before/during widthing dtypes are not resolved + dtypeSetLogicSized(width, widthMin, numeric); + } else { + if (width==dtypep()->width() + && widthMin==dtypep()->widthMin() + && numeric==dtypep()->numeric()) return; // Correct already + // FUTURE: We may be pointing at a two state data type, and this may + // convert it to logic. Since the AstVar remains correct, we + // work OK but this assumption may break in the future. + // Note we can't just clone and do a widthForce, as if it's a BasicDType + // the msb() indications etc will be incorrect. + dtypeSetLogicSized(width, widthMin, numeric); + } +} + +AstNodeDType* AstNode::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) { + // For 'simple' types we use the global directory. These are all unsized. + // More advanced types land under the module/task/etc + return v3Global.rootp()->typeTablep() + ->findBasicDType(fl, kwd); +} +AstNodeDType* AstNode::findBitDType(FileLine* fl, int width, int widthMin, AstNumeric numeric) { + return v3Global.rootp()->typeTablep() + ->findLogicBitDType(fl, AstBasicDTypeKwd::BIT, width, widthMin, numeric); +} +AstNodeDType* AstNode::findLogicDType(FileLine* fl, int width, int widthMin, AstNumeric numeric) { + return v3Global.rootp()->typeTablep() + ->findLogicBitDType(fl, AstBasicDTypeKwd::LOGIC, width, widthMin, numeric); +} +AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { + return v3Global.rootp()->typeTablep() + ->findInsertSameDType(nodep); +} + //###################################################################### // AstNVisitor diff --git a/src/V3Ast.h b/src/V3Ast.h index a363deb0e..0f665272a 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -63,7 +63,7 @@ public: enum VSignedState { // This can't be in the fancy class as the lexer union will get upset - signedst_NOSIGN=0, signedst_UNSIGNED=1, signedst_SIGNED=2 + signedst_UNSIGNED=0, signedst_SIGNED=1, signedst_NOSIGN=2 }; //###################################################################### @@ -73,24 +73,28 @@ public: enum en { UNSIGNED, SIGNED, - DOUBLE, + NOSIGN, _ENUM_MAX // Leave last - // Limited to 2 bits, unless change V3Ast's packing function }; enum en m_e; const char* ascii() const { static const char* names[] = { - "UNSIGNED","SIGNED","DOUBLE" + "UNSIGNED","SIGNED","NOSIGN" }; return names[m_e]; }; inline AstNumeric () : m_e(UNSIGNED) {} inline AstNumeric (en _e) : m_e(_e) {} + inline AstNumeric (VSignedState signst) { + if (signst==signedst_UNSIGNED) m_e=UNSIGNED; + else if (signst==signedst_SIGNED) m_e=SIGNED; + else m_e=NOSIGN; + } explicit inline AstNumeric (int _e) : m_e(static_cast(_e)) {} operator en () const { return m_e; } - inline bool isDouble() const { return m_e==DOUBLE; } inline bool isSigned() const { return m_e==SIGNED; } - inline bool isUnsigned() const { return m_e==UNSIGNED; } + inline bool isNosign() const { return m_e==NOSIGN; } + // No isUnsigned() as it's ambiguous if NOSIGN should be included or not. }; inline bool operator== (AstNumeric lhs, AstNumeric rhs) { return (lhs.m_e == rhs.m_e); } inline bool operator== (AstNumeric lhs, AstNumeric::en rhs) { return (lhs.m_e == rhs); } @@ -261,6 +265,8 @@ public: STRING, // Internal types for mid-steps SCOPEPTR, CHARPTR, + // Unsigned and two state; fundamental types + UINT32, UINT64, // Internal types, eliminated after parsing LOGIC_IMPLICIT, // Leave last @@ -274,6 +280,7 @@ public: "real", "shortint", "shortreal", "time", "string", "VerilatedScope*", "char*", + "IData", "QData", "LOGIC_IMPLICIT", " MAX" }; @@ -286,7 +293,8 @@ public: "double", "short int", "float", "long long", "const char*", "dpiScope", "const char*", - "", + "IData", "QData", + "svLogic", // Though shouldn't be needed " MAX" }; return names[m_e]; @@ -315,11 +323,18 @@ public: case STRING: return 64; // opaque // Just the pointer, for today case SCOPEPTR: return 0; // opaque case CHARPTR: return 0; // opaque + case UINT32: return 32; + case UINT64: return 64; default: return 0; } } bool isSigned() const { - return m_e==BYTE || m_e==SHORTINT || m_e==INT || m_e==LONGINT || m_e==INTEGER; + return m_e==BYTE || m_e==SHORTINT || m_e==INT || m_e==LONGINT || m_e==INTEGER + || m_e==DOUBLE || m_e==FLOAT; + } + bool isUnsigned() const { + return m_e==CHANDLE || m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR + || m_e==UINT32 || m_e==UINT64; } bool isFourstate() const { return m_e==INTEGER || m_e==LOGIC || m_e==LOGIC_IMPLICIT; @@ -328,6 +343,10 @@ public: return (m_e==BIT || m_e==BYTE || m_e==CHANDLE || m_e==INT || m_e==LONGINT || m_e==SHORTINT || m_e==STRING || m_e==DOUBLE || m_e==FLOAT); } + bool isIntNumeric() const { // Enum increment supported + return (m_e==BIT || m_e==BYTE || m_e==INT || m_e==INTEGER || m_e==LOGIC + || m_e==LONGINT || m_e==SHORTINT || m_e==UINT32 || m_e==UINT64); + } bool isSloppy() const { // Don't be as anal about width warnings return !(m_e==LOGIC || m_e==BIT); } @@ -503,6 +522,83 @@ public: inline bool operator== (AstParseRefExp::en lhs, AstParseRefExp rhs) { return (lhs == rhs.m_e); } inline ostream& operator<<(ostream& os, AstParseRefExp rhs) { return os<= LSB + int m_lsb; // LSB + union { + int mu_flags; + struct { + bool m_ranged:1; // Has a range + bool m_littleEndian:1; // Bit vector is little endian + }; + }; + inline bool operator== (const VNumRange& rhs) const { + return m_msb == rhs.m_msb + && m_lsb == rhs.m_lsb + && mu_flags == rhs.mu_flags; } + inline bool operator< (const VNumRange& rhs) const { + if ( (m_msb < rhs.m_msb)) return true; + if (!(m_msb == rhs.m_msb)) return false; // lhs > rhs + if ( (m_lsb < rhs.m_lsb)) return true; + if (!(m_lsb == rhs.m_lsb)) return false; // lhs > rhs + if ( (mu_flags < rhs.mu_flags)) return true; + if (!(mu_flags == rhs.mu_flags)) return false; // lhs > rhs + return false; + } + // + VNumRange() : m_msb(0), m_lsb(0), m_ranged(false), m_littleEndian(false) {} + ~VNumRange() {} + // MEMBERS + void init(int msb, int lsb, bool littleEndian) { + m_msb=msb; m_lsb=lsb; mu_flags=0; m_ranged=true; m_littleEndian=littleEndian; + } + int msb() const { return m_msb; } + int lsb() const { return m_lsb; } + int left() const { return littleEndian()?lsb():msb(); } // How to show a declaration + int right() const { return littleEndian()?msb():lsb(); } + bool ranged() const { return m_ranged; } + bool littleEndian() const { return m_littleEndian; } + bool representableByWidth() const // Could be represented by just width=1, or [width-1:0] + { return (!m_ranged || (m_lsb==0 && m_msb>=1 && !m_littleEndian)); } +}; + +//###################################################################### + +struct VBasicTypeKey { + int m_width; // From AstNodeDType: Bit width of operation + int m_widthMin; // From AstNodeDType: If unsized, bitwidth of minimum implementation + AstNumeric m_numeric; // From AstNodeDType: Node is signed + AstBasicDTypeKwd m_keyword; // From AstBasicDType: What keyword created basic type + VNumRange m_nrange; // From AstBasicDType: Numeric msb/lsb (if non-opaque keyword) + inline bool operator== (const VBasicTypeKey& rhs) const { + return m_width == rhs.m_width + && m_widthMin == rhs.m_widthMin + && m_numeric == rhs.m_numeric + && m_keyword == rhs.m_keyword + && m_nrange == rhs.m_nrange; } + inline bool operator< (const VBasicTypeKey& rhs) const { + if ( (m_width < rhs.m_width)) return true; + if (!(m_width == rhs.m_width)) return false; // lhs > rhs + if ( (m_widthMin < rhs.m_widthMin)) return true; + if (!(m_widthMin == rhs.m_widthMin)) return false; // lhs > rhs + if ( (m_numeric < rhs.m_numeric)) return true; + if (!(m_numeric == rhs.m_numeric)) return false; // lhs > rhs + if ( (m_keyword < rhs.m_keyword)) return true; + if (!(m_keyword == rhs.m_keyword)) return false; // lhs > rhs + if ( (m_nrange < rhs.m_nrange)) return true; + if (!(m_nrange == rhs.m_nrange)) return false; // lhs > rhs + return false; } + VBasicTypeKey(int width, int widthMin, AstNumeric numeric, AstBasicDTypeKwd kwd, + VNumRange nrange) + : m_width(width), m_widthMin(widthMin), m_numeric(numeric), + m_keyword(kwd), m_nrange(nrange) {} + ~VBasicTypeKey() {} +}; + //###################################################################### // AstNUser - Generic pointer base class for AST User nodes. // - Also used to allow parameter passing up/down iterate calls @@ -748,18 +844,17 @@ class AstNode { static vluint64_t s_editCntGbl; // Global edit counter static vluint64_t s_editCntLast;// Global edit counter, last value for printing * near node #s + AstNodeDType* m_dtypep; // Data type of output or assignment (etc) + AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY) int m_cloneCnt; // Mark of when userp was set static int s_cloneCntGbl; // Count of which userp is set // Attributes - uint32_t m_numeric:2; // Node is real/signed - important that bitfields remain unsigned bool m_didWidth:1; // Did V3Width computation bool m_doingWidth:1; // Inside V3Width // // Space for more bools here - int m_width; // Bit width of operation - int m_widthMin; // If unsized, bitwidth of minimum implementation // This member ordering both allows 64 bit alignment and puts associated data together AstNUser* m_user1p; // Pointer to any information the user iteration routine wants uint32_t m_user1Cnt; // Mark of when userp was set @@ -833,6 +928,7 @@ public: AstNode* op2p() const { return m_op2p; } AstNode* op3p() const { return m_op3p; } AstNode* op4p() const { return m_op4p; } + AstNodeDType* dtypep() const { return m_dtypep; } AstNode* clonep() const { return ((m_cloneCnt==s_cloneCntGbl)?m_clonep:NULL); } AstNode* firstAbovep() const { return ((backp() && backp()->nextp()!=this) ? backp() : NULL); } // Returns NULL when second or later in list bool brokeExists() const; @@ -873,26 +969,19 @@ public: string prettyTypeName() const; // "VARREF name" for error messages FileLine* fileline() const { return m_fileline; } void fileline(FileLine* fl) { m_fileline=fl; } - int width() const { return m_width; } - bool width1() const { return width()==1; } - int widthWords() const { return VL_WORDS_I(width()); } - int widthMin() const { return m_widthMin?m_widthMin:m_width; } // If sized, the size, if unsized the min digits to represent it - int widthPow2() const; - int widthInstrs() const { return isWide()?widthWords():1; } - bool widthSized() const { return !m_widthMin || m_widthMin==m_width; } - void width(int width, int sized) { m_width=width; m_widthMin=sized; } - void widthFrom(AstNode* fromp) { if (fromp) { m_width=fromp->m_width; m_widthMin=fromp->m_widthMin; }} - void widthSignedFrom(AstNode* fromp) { widthFrom(fromp); numericFrom(fromp); } - void numericFrom(AstNode* fromp) { numeric(fromp->numeric()); } - void numeric(AstNumeric flag) { m_numeric = (int)flag; if (flag.isDouble()) width(64,64); } - AstNumeric numeric() const { return AstNumeric(m_numeric); } - void dtypeFrom(AstNode* fromp) { if (fromp) { widthSignedFrom(fromp); }} - bool isUnsigned() const { return numeric().isUnsigned(); } + bool width1() const; + int widthInstrs() const; void didWidth(bool flag) { m_didWidth=flag; } bool didWidth() const { return m_didWidth; } bool didWidthAndSet() { if (didWidth()) return true; didWidth(true); return false;} void doingWidth(bool flag) { m_doingWidth=flag; } bool doingWidth() const { return m_doingWidth; } + + //TODO stomp these width functions out, and call via dtypep() instead + int width() const; + int widthMin() const; + int widthWords() const { return VL_WORDS_I(width()); } + int widthPow2() const; bool isQuad() const { return (width()>VL_WORDSIZE && width()<=VL_QUADSIZE); } bool isWide() const { return (width()>VL_QUADSIZE); } bool isDouble() const; @@ -965,14 +1054,18 @@ public: bool isAllOnesV(); // Verilog width rules apply // METHODS - data type changes especially for initial creation - void dtypeChgSigned(bool flag) { numeric(flag ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); } - void dtypeSetBitSized(int widthf, int widthMinf, AstNumeric numericf) { numeric(numericf); width(widthf,widthMinf); } - void dtypeSetLogicSized(int widthf, int widthMinf, AstNumeric numericf) { numeric(numericf); width(widthf,widthMinf); } - void dtypeSetLogicBool() { numeric(AstNumeric::UNSIGNED); width(1,1); } - void dtypeSetDouble() { numeric(AstNumeric::DOUBLE); } - void dtypeSetSigned32() { numeric(AstNumeric::SIGNED); width(VL_WORDSIZE,VL_WORDSIZE); } - void dtypeSetUInt32() { numeric(AstNumeric::UNSIGNED); width(VL_WORDSIZE,VL_WORDSIZE); } - void dtypeSetUInt64() { numeric(AstNumeric::UNSIGNED); width(VL_QUADSIZE,VL_QUADSIZE); } + void dtypep(AstNodeDType* nodep) { if (m_dtypep != nodep) { m_dtypep = nodep; editCountInc(); } } + void dtypeFrom(AstNode* fromp) { if (fromp) { dtypep(fromp->dtypep()); }} + void dtypeChgSigned(bool flag=true); + void dtypeChgWidth(int width, int widthMin); + void dtypeChgWidthSigned(int width, int widthMin, bool issigned); + void dtypeSetBitSized(int width, int widthMin, AstNumeric numeric) { dtypep(findBitDType(fileline(),width,widthMin,numeric)); } + void dtypeSetLogicSized(int width, int widthMin, AstNumeric numeric) { dtypep(findLogicDType(fileline(),width,widthMin,numeric)); } + void dtypeSetLogicBool() { dtypep(findBasicDType(fileline(),AstBasicDTypeKwd::LOGIC)); } + void dtypeSetDouble() { dtypep(findBasicDType(fileline(),AstBasicDTypeKwd::DOUBLE)); } + void dtypeSetSigned32() { dtypep(findBasicDType(fileline(),AstBasicDTypeKwd::INTEGER)); } + void dtypeSetUInt32() { dtypep(findBasicDType(fileline(),AstBasicDTypeKwd::UINT32)); } // Twostate + void dtypeSetUInt64() { dtypep(findBasicDType(fileline(),AstBasicDTypeKwd::UINT64)); } // Twostate // METHODS - dump and error void v3errorEnd(ostringstream& str) const; @@ -996,6 +1089,12 @@ public: virtual void addNextStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument + // Data type locators + static AstNodeDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd); + static AstNodeDType* findBitDType(FileLine* fl, int width, int widthMin, AstNumeric numeric); + static AstNodeDType* findLogicDType(FileLine* fl, int width, int widthMin, AstNumeric numeric); + static AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + // METHODS - Iterate on a tree AstNode* cloneTree(bool cloneNextLink); bool sameTree(AstNode* node2p); // Does tree of this == node2p? @@ -1020,6 +1119,7 @@ public: virtual V3Hash sameHash() const { return V3Hash(V3Hash::Illegal()); } // Not a node that supports it virtual bool same(AstNode* otherp) const { return true; } virtual bool hasDType() const { return false; } // Iff has a data type; dtype() must be non null + virtual AstNodeDType* getChildDTypep() const { return NULL; } // Iff has a non-null childDTypep(), as generic node function virtual bool maybePointedTo() const { return false; } // Another AstNode* may have a pointer into this node, other then normal front/back/etc. virtual bool broken() const { return false; } @@ -1362,20 +1462,55 @@ public: }; struct AstNodeDType : public AstNode { - // Data type - AstNodeDType(FileLine* fl) : AstNode(fl) {} +private: + // Ideally width() would migrate to BasicDType as that's where it makes sense, + // but it's currently so prevalent in the code we leave it here. + // Note the below members are included in AstTypeTable::Key lookups + int m_width; // (also in AstTypeTable::Key) Bit width of operation + int m_widthMin; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation + AstNumeric m_numeric; // (also in AstTypeTable::Key) Node is signed + // Other members + bool m_generic; // Simple globally referenced type, don't garbage collect + static int s_uniqueNum; // Unique number assigned to each dtype during creation for IEEE matching +public: + // CONSTRUCTORS + AstNodeDType(FileLine* fl) : AstNode(fl) { + m_width=0; m_widthMin=0; m_generic=false; + } ASTNODE_BASE_FUNCS(NodeDType) - // Accessors + // ACCESSORS virtual void dump(ostream& str); + virtual void dumpSmall(ostream& str); virtual bool hasDType() const { return true; } virtual AstBasicDType* basicp() const = 0; // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs to next non-typeref type virtual int widthAlignBytes() const = 0; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) virtual int widthTotalBytes() const = 0; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... virtual bool maybePointedTo() const { return true; } + virtual AstNodeDType* virtRefDTypep() const { return NULL; } // Iff has a non-null refDTypep(), as generic node function + virtual void virtRefDTypep(AstNodeDType* nodep) { } // Iff has refDTypep(), set as generic node function + // + // Changing the width may confuse the data type resolution, so must clear TypeTable cache after use. + void widthForce(int width, int sized) { m_width=width; m_widthMin=sized; } + // For backward compatibility AstArrayDType and others inherit width and signing from the subDType/base type + void widthFromSub(AstNodeDType* nodep) { m_width=nodep->m_width; m_widthMin=nodep->m_widthMin; m_numeric=nodep->m_numeric; } + // + int width() const { return m_width; } + void numeric(AstNumeric flag) { m_numeric = flag; } + bool isSigned() const { return m_numeric.isSigned(); } + bool isNosign() const { return m_numeric.isNosign(); } + AstNumeric numeric() const { return m_numeric; } + int widthWords() const { return VL_WORDS_I(width()); } + int widthMin() const { return m_widthMin?m_widthMin:m_width; } // If sized, the size, if unsized the min digits to represent it + int widthPow2() const; + void widthMinFromWidth() { m_widthMin = m_width; } + bool widthSized() const { return !m_widthMin || m_widthMin==m_width; } + bool generic() const { return m_generic; } + void generic(bool flag) { m_generic = flag; } AstNodeDType* dtypeDimensionp(int depth); pair dimensions(); uint32_t arrayElements(); // 1, or total multiplication of all dimensions + static int uniqueNumInc() { return ++s_uniqueNum; } }; struct AstNodeSel : public AstNodeBiop { @@ -1552,8 +1687,12 @@ public: //###################################################################### // Inline ACCESSORS -inline bool AstNode::isDouble() const { return numeric().isDouble(); } -inline bool AstNode::isSigned() const { return numeric().isSigned(); } +inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; } +inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; } +inline bool AstNode::width1() const { return dtypep() && dtypep()->width()==1; } // V3Const uses to know it can optimize +inline int AstNode::widthInstrs() const { return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); } +inline bool AstNode::isDouble() const { return dtypep() && dtypep()->castBasicDType() && dtypep()->castBasicDType()->isDouble(); } +inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); } inline bool AstNode::isZero() { return (this->castConst() && this->castConst()->num().isEqZero()); } inline bool AstNode::isNeqZero() { return (this->castConst() && this->castConst()->num().isNeqZero()); } @@ -1561,6 +1700,6 @@ inline bool AstNode::isOne() { return (this->castConst() && this->castConst inline bool AstNode::isAllOnes() { return (this->castConst() && this->castConst()->isEqAllOnes()); } inline bool AstNode::isAllOnesV() { return (this->castConst() && this->castConst()->isEqAllOnesV()); } -inline void AstNodeVarRef::init() { if (m_varp) widthSignedFrom((AstNode*)m_varp); } +inline void AstNodeVarRef::init() { if (m_varp) dtypep(m_varp->dtypep()); } #endif // Guard diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index a5162832e..3e8d1e886 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -440,6 +440,87 @@ bool AstSenTree::hasCombo() { return false; } +void AstTypeTable::clearCache() { + // When we mass-change widthMin in V3WidthCommit, we need to correct the table. + // Just clear out the maps; the search functions will be used to rebuild the map + for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) { + m_basicps[i] = NULL; + } + for (int isbit=0; isbit<_IDX0_MAX; ++isbit) { + for (int numer=0; numernextp()) { + if (AstBasicDType* bdtypep = nodep->castBasicDType()) { + bdtypep->generic(false); + } + } +} + +void AstTypeTable::repairCache() { + // After we mass-change widthMin in V3WidthCommit, we need to correct the table. + clearCache(); + for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) { + if (AstBasicDType* bdtypep = nodep->castBasicDType()) { + (void)findInsertSameDType(bdtypep); + } + } +} + +AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) { + if (m_basicps[kwd]) return m_basicps[kwd]; + // + AstBasicDType* new1p = new AstBasicDType(fl, kwd); + // Because the detailed map doesn't update this map, + // check the detailed map for this same node + // Also adds this new node to the detailed map + AstBasicDType* newp = findInsertSameDType(new1p); + if (newp != new1p) new1p->deleteTree(); + else addTypesp(newp); + // + m_basicps[kwd] = newp; + return newp; +} + +AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, + int width, int widthMin, AstNumeric numeric) { + int idx = IDX0_LOGIC; + if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC; + else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT; + else fl->v3fatalSrc("Bad kwd for findLogicBitDType"); + pair widths = make_pair(width,widthMin); + LogicMap& mapr = m_logicMap[idx][(int)numeric]; + LogicMap::const_iterator it = mapr.find(widths); + if (it != mapr.end()) return it->second; + // + AstBasicDType* new1p = new AstBasicDType(fl, AstBasicDTypeKwd::BIT, numeric, width, widthMin); + // Because the detailed map doesn't update this map, + // check the detailed map for this same node, and if found update this map + // Also adds this new node to the detailed map + AstBasicDType* newp = findInsertSameDType(new1p); + if (newp != new1p) new1p->deleteTree(); + else addTypesp(newp); + // + mapr.insert(make_pair(widths,newp)); + return newp; +} + +AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) { + VBasicTypeKey key (nodep->width(), nodep->widthMin(), nodep->numeric(), + nodep->keyword(), nodep->nrange()); + DetailedMap& mapr = m_detailedMap; + DetailedMap::const_iterator it = mapr.find(key); + if (it != mapr.end()) return it->second; + mapr.insert(make_pair(key,nodep)); + nodep->generic(true); + // No addTypesp; the upper function that called new() is responsible for adding + return nodep; +} + //====================================================================== // Special walking tree inserters @@ -516,10 +597,15 @@ void AstNode::dump(ostream& str) { if (user3p()) str<<" u3="<<(void*)user3p(); if (user4p()) str<<" u4="<<(void*)user4p(); if (user5p()) str<<" u5="<<(void*)user5p(); - str<<" "<<(isSigned()?"s":"") - <<(isDouble()?"d":"") - <<"w"<<(widthSized()?"":"u")<AstNodeDType::dump(str); str<<" kwd="<AstNode::dump(str); @@ -597,6 +682,21 @@ void AstVarXRef::dump(ostream& str) { } void AstNodeDType::dump(ostream& str) { this->AstNode::dump(str); + if (generic()) str<<" [GENERIC]"; + if (AstNodeDType* dtp = virtRefDTypep()) { + str<<" refdt="<<(void*)(dtp); + dtp->dumpSmall(str); + } +} +void AstNodeDType::dumpSmall(ostream& str) { + str<<"(" + <<(generic()?"G/":"") + <<((isSigned()&&!isDouble())?"s":"") + <<(isNosign()?"n":"") + <<(isDouble()?"d":"") + <<"w"<<(widthSized()?"":"u")<AstNode::dump(str); @@ -608,6 +708,45 @@ void AstPackageImport::dump(ostream& str) { this->AstNode::dump(str); str<<" -> "<AstNode::dump(str); + for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) { + if (AstBasicDType* subnodep=m_basicps[i]) { + str< "; + subnodep->dump(str); + } + } + for (int isbit=0; isbit<2; ++isbit) { + for (int issigned=0; issignedsecond; + str<first.first<<"/"<first.second; + str<<"\t\t"< "; + dtypep->dump(str); + } + } + } + { + DetailedMap& mapr = m_detailedMap; + for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) { + AstBasicDType* dtypep = it->second; + str< "; + dtypep->dump(str); + } + } + // Note get newline from caller too. +} + void AstVarScope::dump(ostream& str) { this->AstNode::dump(str); if (isCircular()) str<<" [CIRC]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index c9ca0a047..cdbdb9c6e 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -164,13 +164,14 @@ private: public: AstTypedef(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp) : AstNode(fl), m_name(name) { - setOp1p(dtp); - widthSignedFrom(dtp); + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve } ASTNODE_NODE_FUNCS(Typedef, TYPEDEF) - AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable - void dtypep(AstNodeDType* nodep) { setOp1p(nodep); } - AstNodeDType* subDTypep() const { return dtypep(); } + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } // METHODS virtual string name() const { return m_name; } virtual bool maybePointedTo() const { return true; } @@ -197,20 +198,26 @@ struct AstDefImplicitDType : public AstNodeDType { private: string m_name; void* m_containerp; // In what scope is the name unique, so we can know what are duplicate definitions (arbitrary value) + int m_uniqueNum; public: AstDefImplicitDType(FileLine* fl, const string& name, AstNode* containerp, VFlagChildDType, AstNodeDType* dtp) : AstNodeDType(fl), m_name(name), m_containerp(containerp) { - setOp1p(dtp); - widthSignedFrom(dtp); + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve + m_uniqueNum = uniqueNumInc(); } ASTNODE_NODE_FUNCS(DefImplicitDType, DEFIMPLICITDTYPE) - AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable - AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable - void dtypep(AstNodeDType* nodep) { setOp1p(nodep); } + virtual bool same(AstNode* samep) const { return m_uniqueNum==samep->castDefImplicitDType()->m_uniqueNum; } + virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); } + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } void* containerp() const { return m_containerp; } // METHODS - virtual AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type + AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable + virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); } virtual int widthTotalBytes() const { return dtypep()->widthTotalBytes(); } @@ -220,34 +227,55 @@ public: struct AstArrayDType : public AstNodeDType { // Array data type, ie "some_dtype var_name [2:0]" + // Children: DTYPE (moved to refDTypep() in V3Width) + // Children: RANGE (array bounds) private: + AstNodeDType* m_refDTypep; // Elements of this type (after widthing) bool m_packed; public: AstArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep, bool isPacked=false) : AstNodeDType(fl), m_packed(isPacked) { - setOp1p(dtp); + childDTypep(dtp); // Only for parser + refDTypep(NULL); setOp2p(rangep); - widthSignedFrom(dtp); + dtypep(NULL); // V3Width will resolve + widthFromSub(subDTypep()); } AstArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep, bool isPacked=false) : AstNodeDType(fl), m_packed(isPacked) { - setOp1p(dtp); + refDTypep(dtp); setOp2p(rangep); - widthSignedFrom(dtp); + dtypep(this); + widthFromSub(subDTypep()); } ASTNODE_NODE_FUNCS(ArrayDType, ARRAYDTYPE) virtual void dump(ostream& str); - AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable - AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable - void dtypep(AstNodeDType* nodep) { setOp1p(nodep); } - AstNodeDType* subDTypep() const { return dtypep(); } + virtual bool broken() const { return !((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) + || (!m_refDTypep && childDTypep())); } + virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) { + m_refDTypep = m_refDTypep->clonep()->castNodeDType(); + }} + virtual bool same(AstNode* samep) const { + AstArrayDType* sp = samep->castArrayDType(); + return (m_packed==sp->m_packed + && msb()==sp->msb() + && subDTypep()==sp->subDTypep() + && rangep()->sameTree(sp->rangep())); } // HashedDT doesn't recurse, so need to check children + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(msb()),V3Hash(lsb())); } + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } + void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } + virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } + virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } AstRange* rangep() const { return op2p()->castRange(); } // op2 = Array(s) of variable void rangep(AstRange* nodep) { setOp2p(nodep); } // METHODS - virtual AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type + virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } - virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); } - virtual int widthTotalBytes() const { return elementsConst() * dtypep()->widthTotalBytes(); } + virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } + virtual int widthTotalBytes() const { return elementsConst() * subDTypep()->widthTotalBytes(); } int msb() const { return rangep()->msbConst(); } int lsb() const { return rangep()->lsbConst(); } int elementsConst() const { return rangep()->elementsConst(); } @@ -259,109 +287,144 @@ struct AstBasicDType : public AstNodeDType { // Builtin atomic/vectored data type // Children: RANGE (converted to constant in V3Width) private: - AstBasicDTypeKwd m_keyword; // What keyword created it - bool m_implicit; // Implicitly declared - bool m_nosigned; // Implicit without sign - int m_msb; // MSB when no range attached + struct Members { + AstBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type + VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword) + bool operator== (const Members& rhs) const { + return rhs.m_keyword == m_keyword + && rhs.m_nrange == m_nrange; } + } m; + // See also in AstNodeDtype: m_width, m_widthMin, m_numeric(issigned) public: AstBasicDType(FileLine* fl, AstBasicDTypeKwd kwd, VSignedState signst=signedst_NOSIGN) : AstNodeDType(fl) { - init(kwd, signst, 0, NULL); + init(kwd, AstNumeric(signst), 0, -1, NULL); } AstBasicDType(FileLine* fl, VFlagLogicPacked, int wantwidth) : AstNodeDType(fl) { - init(AstBasicDTypeKwd::LOGIC, signedst_NOSIGN, wantwidth, NULL); + init(AstBasicDTypeKwd::LOGIC, AstNumeric::NOSIGN, wantwidth, -1, NULL); } AstBasicDType(FileLine* fl, VFlagBitPacked, int wantwidth) : AstNodeDType(fl) { - init(AstBasicDTypeKwd::BIT, signedst_NOSIGN, wantwidth, NULL); + init(AstBasicDTypeKwd::BIT, AstNumeric::NOSIGN, wantwidth, -1, NULL); + } + AstBasicDType(FileLine* fl, AstBasicDTypeKwd kwd, AstNumeric numer, int wantwidth, int widthmin) + : AstNodeDType(fl) { + init(kwd, numer, wantwidth, widthmin, NULL); } // See also addRange in verilog.y private: - void init(AstBasicDTypeKwd kwd, VSignedState signst, int wantwidth, AstRange* rangep) { - m_keyword = kwd; - m_msb = 0; + void init(AstBasicDTypeKwd kwd, AstNumeric numer, + int wantwidth, int wantwidthmin, AstRange* rangep) { + // wantwidth=0 means figure it out, but if a widthmin is >=0 + // we allow width 0 so that {{0{x}},y} works properly + // wantwidthmin=-1: default, use wantwidth if it is non zero + m.m_keyword = kwd; // Implicitness: // "parameter X" is implicit and sized from initial value, "parameter reg x" not - m_implicit = false; - m_nosigned = false; if (keyword()==AstBasicDTypeKwd::LOGIC_IMPLICIT) { - if (!rangep && !wantwidth) m_implicit = true; // Also cleared if range added later - m_keyword = AstBasicDTypeKwd::LOGIC; + if (rangep || wantwidth) m.m_keyword = AstBasicDTypeKwd::LOGIC; } - if (signst == signedst_NOSIGN) { - if (keyword().isSigned()) signst = signedst_SIGNED; - else m_nosigned = true; + if (numer == AstNumeric::NOSIGN) { + if (keyword().isSigned()) numer = AstNumeric::SIGNED; + else if (keyword().isUnsigned()) numer = AstNumeric::UNSIGNED; } - if (keyword().isDouble()) dtypeSetDouble(); - else setSignedState(signst); - if (!rangep && wantwidth) { // Constant width - m_msb = wantwidth - 1; - width(wantwidth, wantwidth); + numeric(numer); + if (!rangep && (wantwidth || wantwidthmin>=0)) { // Constant width + if (wantwidth>1) m.m_nrange.init(wantwidth-1, 0, false); + int wmin = wantwidthmin>=0 ? wantwidthmin : wantwidth; + widthForce(wantwidth, wmin); } else if (!rangep) { // Set based on keyword properties // V3Width will pull from this width - if (keyword().width() > 1 && !isOpaque()) rangep = new AstRange(fileline(), keyword().width()-1, 0); - width(keyword().width(), keyword().width()); + if (keyword().width() > 1 && !isOpaque()) { + m.m_nrange.init(keyword().width()-1, 0, false); + } + widthForce(keyword().width(), keyword().width()); } else { - width(rangep->elementsConst(), rangep->elementsConst()); // Maybe unknown if parameters underneath it + widthForce(rangep->elementsConst(), rangep->elementsConst()); // Maybe unknown if parameters underneath it } setNOp1p(rangep); + dtypep(this); } public: ASTNODE_NODE_FUNCS(BasicDType, BASICDTYPE) virtual void dump(ostream& str); - virtual V3Hash sameHash() const { return V3Hash(keyword()); } - virtual bool same(AstNode* samep) const { - return samep->castBasicDType()->keyword() == keyword(); } - virtual string name() const { return m_keyword.ascii(); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m.m_keyword), V3Hash(m.m_nrange.msb())); } + virtual bool same(AstNode* samep) const { // width/widthMin/numeric compared elsewhere + return samep->castBasicDType()->m == m; } + virtual string name() const { return m.m_keyword.ascii(); } + virtual bool broken() const { return dtypep()!=this; } AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range of variable void rangep(AstRange* nodep) { setNOp1p(nodep); } - void setSignedState(VSignedState signst) { - if (signst==signedst_UNSIGNED) numeric(AstNumeric::UNSIGNED); - else if (signst==signedst_SIGNED) numeric(AstNumeric::SIGNED); + void setSignedState(VSignedState signst) { + // Note NOSIGN does NOT change the state; this is required by the parser + if (signst==signedst_UNSIGNED) numeric(VSignedState(signst)); + else if (signst==signedst_SIGNED) numeric(VSignedState(signst)); } // METHODS virtual AstBasicDType* basicp() const { return (AstBasicDType*)this; } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... - AstBasicDTypeKwd keyword() const { return m_keyword; } // Avoid using - use isSomething accessors instead + AstBasicDTypeKwd keyword() const { return m.m_keyword; } // Avoid using - use isSomething accessors instead bool isBitLogic() const { return keyword().isBitLogic(); } bool isDouble() const { return keyword().isDouble(); } bool isOpaque() const { return keyword().isOpaque(); } bool isSloppy() const { return keyword().isSloppy(); } bool isZeroInit() const { return keyword().isZeroInit(); } - bool isRanged() const { return rangep() || m_msb; } - int msb() const { if (!rangep()) return m_msb; return rangep()->msbConst(); } - int lsb() const { if (!rangep()) return 0; return rangep()->lsbConst(); } - int left() const { if (!rangep()) return m_msb; return littleEndian()?rangep()->lsbConst():rangep()->msbConst(); } - int right() const { if (!rangep()) return 0; return littleEndian()?rangep()->msbConst():rangep()->lsbConst(); } + bool isRanged() const { return rangep() || m.m_nrange.ranged(); } + const VNumRange& nrange() const { return m.m_nrange; } // Generally the msb/lsb/etc funcs should be used instead + int msb() const { return (rangep() ? rangep()->msbConst() : m.m_nrange.msb()); } + int lsb() const { return (rangep() ? rangep()->lsbConst() : m.m_nrange.lsb()); } + int left() const { return littleEndian()?lsb():msb(); } // How to show a declaration + int right() const { return littleEndian()?msb():lsb(); } int msbMaxSelect() const { return (lsb()<0 ? msb()-lsb() : msb()); } // Maximum value a [] select may index - bool littleEndian() const { return (rangep() && rangep()->littleEndian()); } - bool implicit() const { return m_implicit; } - void implicit(bool flag) { m_implicit = flag; } - bool nosigned() const { return m_nosigned; } - void cvtRangeConst() {} // Convert to smaller represenation - disabled + bool littleEndian() const { return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian()); } + bool implicit() const { return keyword() == AstBasicDTypeKwd::LOGIC_IMPLICIT; } + void cvtRangeConst() { // Convert to smaller represenation + if (rangep() && rangep()->msbp()->castConst() && rangep()->lsbp()->castConst()) { + m.m_nrange.init(rangep()->msbConst(), rangep()->lsbConst(), + rangep()->littleEndian()); + rangep()->unlinkFrBackWithNext()->deleteTree(); + rangep(NULL); + } + } }; struct AstConstDType : public AstNodeDType { // const data type, ie "const some_dtype var_name [2:0]" // ConstDType are removed in V3LinkLValue and become AstVar::isConst. // When more generic types are supported AstConstDType will be propagated further. +private: + AstNodeDType* m_refDTypep; // Inherit from this base data type public: AstConstDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp) : AstNodeDType(fl) { - setOp1p(dtp); - widthSignedFrom(dtp); + childDTypep(dtp); // Only for parser + refDTypep(NULL); // V3Width will resolve + dtypep(NULL); // V3Width will resolve + widthFromSub(subDTypep()); } ASTNODE_NODE_FUNCS(ConstDType, CONSTDTYPE) - AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable - void dtypep(AstNodeDType* nodep) { setOp1p(nodep); } - AstNodeDType* subDTypep() const { return dtypep(); } + virtual bool broken() const { return !((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) + || (!m_refDTypep && childDTypep())); } + virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) { + m_refDTypep = m_refDTypep->clonep()->castNodeDType(); + }} + virtual bool same(AstNode* samep) const { + return (m_refDTypep==samep->castConstDType()->m_refDTypep); } + virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); } // node's type() included elsewhere + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable + void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } + virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } + virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } // METHODS - virtual AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type + virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } - virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); } - virtual int widthTotalBytes() const { return dtypep()->widthTotalBytes(); } + virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } + virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); } }; struct AstRefDType : public AstNodeDType { @@ -374,7 +437,8 @@ public: : AstNodeDType(fl), m_refDTypep(NULL), m_name(name), m_packagep(NULL) {} AstRefDType(FileLine* fl, AstNodeDType* defp) : AstNodeDType(fl), m_refDTypep(defp), m_packagep(NULL) { - widthSignedFrom(defp); + dtypeFrom(defp->dtypep()); + widthFromSub(subDTypep()); } ASTNODE_NODE_FUNCS(RefDType, REFDTYPE) // METHODS @@ -383,11 +447,13 @@ public: m_refDTypep = m_refDTypep->clonep()->castNodeDType(); }} virtual bool same(AstNode* samep) const { - return skipRefp()->sameTree(samep->castRefDType()->skipRefp()); } - virtual V3Hash sameHash() const { return V3Hash(skipRefp()); } + return (m_refDTypep==samep->castRefDType()->m_refDTypep + && m_name==samep->castRefDType()->m_name + && m_packagep==samep->castRefDType()->m_packagep); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(m_packagep)); } virtual void dump(ostream& str=cout); virtual string name() const { return m_name; } - virtual AstBasicDType* basicp() const { return defp() ? defp()->basicp() : NULL; } + virtual AstBasicDType* basicp() const { return subDTypep() ? subDTypep()->basicp() : NULL; } virtual AstNodeDType* skipRefp() const { // Skip past both the Ref and the Typedef if (defp()) return defp()->skipRefp(); @@ -396,14 +462,12 @@ public: virtual int widthAlignBytes() const { return dtypeSkipRefp()->widthAlignBytes(); } virtual int widthTotalBytes() const { return dtypeSkipRefp()->widthTotalBytes(); } void name(const string& flag) { m_name = flag; } - AstNodeDType* dtypep() const { - if (defp()) return defp(); - else { v3fatalSrc("Typedef not linked"); return NULL; } - } AstNodeDType* dtypeSkipRefp() const { return defp()->skipRefp(); } // op1 = Range of variable AstNodeDType* defp() const { return m_refDTypep; } // Code backward compatibility name for refDTypep AstNodeDType* refDTypep() const { return m_refDTypep; } void refDTypep(AstNodeDType* nodep) { m_refDTypep=nodep; } + virtual AstNodeDType* virtRefDTypep() const { return refDTypep(); } + virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } AstNodeDType* subDTypep() const { return m_refDTypep; } AstPackage* packagep() const { return m_packagep; } void packagep(AstPackage* nodep) { m_packagep=nodep; } @@ -456,21 +520,41 @@ public: struct AstEnumDType : public AstNodeDType { // Parents: TYPEDEF/MODULE // Children: ENUMVALUEs +private: + AstNodeDType* m_refDTypep; // Elements are of this type after V3Width + int m_uniqueNum; public: AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstNode* itemsp) - : AstNodeDType(fl) - { setOp1p(dtp); addNOp2p(itemsp); } + : AstNodeDType(fl) { + childDTypep(dtp); // Only for parser + refDTypep(NULL); + addNOp2p(itemsp); + dtypep(NULL); // V3Width will resolve + widthFromSub(subDTypep()); + m_uniqueNum = uniqueNumInc(); + } ASTNODE_NODE_FUNCS(EnumDType, ENUMDTYPE) - AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Data type - void dtypep(AstNodeDType* nodep) { setOp1p(nodep); } + virtual bool broken() const { return !((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) + || (!m_refDTypep && childDTypep())); } + virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) { + m_refDTypep = m_refDTypep->clonep()->castNodeDType(); + }} + virtual bool same(AstNode* samep) const { return m_uniqueNum==samep->castEnumDType()->m_uniqueNum; } + virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); } + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Data type + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable + void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } + virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } + virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } AstEnumItem* itemsp() const { return op2p()->castEnumItem(); } // op2 = AstEnumItem's void addValuesp(AstNode* nodep) { addOp2p(nodep); } - AstNodeDType* subDTypep() const { return dtypep(); } // METHODS - virtual AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type - virtual AstNodeDType* skipRefp() const { return dtypep()->skipRefp(); } - virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); } - virtual int widthTotalBytes() const { return dtypep()->widthTotalBytes(); } + virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type + virtual AstNodeDType* skipRefp() const { return subDTypep()->skipRefp(); } + virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } + virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); } }; //###################################################################### @@ -482,7 +566,10 @@ private: unsigned m_start; unsigned m_length; void init(AstNode* fromp) { - if (fromp) widthSignedFrom(fromp); + if (fromp && fromp->dtypep()->castArrayDType()) { + // Strip off array to find what array references + dtypeFrom(fromp->dtypep()->castArrayDType()->subDTypep()); + } } public: AstArraySel(FileLine* fl, AstNode* fromp, AstNode* bitp) @@ -580,13 +667,15 @@ struct AstSel : public AstNodeTriop { AstSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, AstNode* widthp) :AstNodeTriop(fl, fromp, lsbp, widthp) { if (widthp->castConst()) { - width(widthp->castConst()->toUInt(), widthp->castConst()->toUInt()); + dtypeSetLogicSized(widthp->castConst()->toUInt(), + widthp->castConst()->toUInt(), + AstNumeric::UNSIGNED); } } AstSel(FileLine* fl, AstNode* fromp, int lsb, int bitwidth) :AstNodeTriop(fl, fromp, new AstConst(fl,lsb), new AstConst(fl,bitwidth)) { - width(bitwidth,bitwidth); + dtypeSetLogicSized(bitwidth,bitwidth,AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(Sel, SEL) virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit, const V3Number& width) { @@ -660,55 +749,47 @@ public: :AstNode(fl) , m_name(name) { init(); - combineType(type); setOp1p(dtp); - if (dtp && dtp->basicp()) { - numericFrom(dtp); - width(dtp->basicp()->width(), 0); - } else width(1, 0); + combineType(type); + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve } AstVar(FileLine* fl, AstVarType type, const string& name, AstNodeDType* dtp) :AstNode(fl) , m_name(name) { init(); - combineType(type); setOp1p(dtp); - if (dtp && dtp->basicp()) { - numericFrom(dtp); - width(dtp->basicp()->width(), 0); - } else width(1, 0); + combineType(type); + UASSERT(dtp,"AstVar created with no dtype"); + dtypep(dtp); } AstVar(FileLine* fl, AstVarType type, const string& name, VFlagLogicPacked, int wantwidth) :AstNode(fl) , m_name(name) { init(); combineType(type); - setOp1p(new AstBasicDType(fl, VFlagLogicPacked(), wantwidth)); - width(wantwidth,0); + dtypep(findLogicDType(fl,wantwidth,wantwidth,AstNumeric::UNSIGNED)); } AstVar(FileLine* fl, AstVarType type, const string& name, VFlagBitPacked, int wantwidth) :AstNode(fl) , m_name(name) { init(); combineType(type); - setOp1p(new AstBasicDType(fl, VFlagBitPacked(), wantwidth)); - width(wantwidth,0); + dtypep(findLogicDType(fl,wantwidth,wantwidth,AstNumeric::UNSIGNED)); } AstVar(FileLine* fl, AstVarType type, const string& name, AstVar* examplep) :AstNode(fl) , m_name(name) { init(); combineType(type); - if (examplep->dtypep()) { - setOp1p(examplep->dtypep()->cloneTree(true)); + if (examplep->childDTypep()) { + childDTypep(examplep->childDTypep()->cloneTree(true)); } - numericFrom(examplep); - width(examplep->width(), examplep->widthMin()); + dtypeFrom(examplep); } ASTNODE_NODE_FUNCS(Var, VAR) virtual void dump(ostream& str); virtual string name() const { return m_name; } // * = Var name virtual bool hasDType() const { return true; } virtual bool maybePointedTo() const { return true; } - virtual bool broken() const { return !dtypep(); } AstVarType varType() const { return m_varType; } // * = Type of variable void varType2Out() { m_tristate=0; m_input=0; m_output=1; } void varType2In() { m_tristate=0; m_input=1; m_output=0; } @@ -719,15 +800,17 @@ public: string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc void combineType(AstVarType type); - AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable - AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) - AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType) + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable + AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) + AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType) AstNode* valuep() const { return op3p()->castNode(); } // op3 = Initial value that never changes (static const) void valuep(AstNode* nodep) { setOp3p(nodep); } // It's valuep, not constp, as may be more complicated than an AstConst void addAttrsp(AstNode* nodep) { addNOp4p(nodep); } AstNode* attrsp() const { return op4p()->castNode(); } // op4 = Attributes during early parse bool hasSimpleInit() const { return (op3p() && !op3p()->castInitArray()); } - void dtypep(AstNodeDType* nodep) { setOp1p(nodep); } + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); dtypep(nodep); } + AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } void attrClockEn(bool flag) { m_attrClockEn = flag; } void attrFileDescr(bool flag) { m_fileDescr = flag; } void attrScClocked(bool flag) { m_scClocked = flag; } @@ -982,6 +1065,7 @@ public: AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, bool lvalue) :AstNodeVarRef(fl, varp->name(), varp, lvalue) , m_dotted(dotted) { + dtypeFrom(varp); } ASTNODE_NODE_FUNCS(VarXRef, VARXREF) virtual void dump(ostream& str); @@ -1015,7 +1099,6 @@ public: m_pinNum = pinNum; m_modVarp = NULL; setNOp1p(exprp); - if (exprp) widthSignedFrom(exprp); } AstPin(FileLine* fl, int pinNum, AstVarRef* varname, AstNode* exprp) :AstNode(fl), m_svImplicit(false) { @@ -1023,7 +1106,6 @@ public: m_pinNum = pinNum; m_modVarp = NULL; setNOp1p(exprp); - if (exprp) widthSignedFrom(exprp); } ASTNODE_NODE_FUNCS(Pin, PIN) virtual void dump(ostream& str); @@ -2274,7 +2356,7 @@ public: AstChangeDet(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool clockReq) : AstNodeStmt(fl) { setNOp1p(lhsp); setNOp2p(rhsp); m_clockReq=clockReq; - if (lhsp) widthSignedFrom(lhsp); } + } ASTNODE_NODE_FUNCS(ChangeDet, CHANGEDET) AstNode* lhsp() const { return op1p(); } AstNode* rhsp() const { return op2p(); } @@ -2387,7 +2469,7 @@ public: , m_showname(showname) { dtypeFrom(varp); m_code = 0; - m_codeInc = varp->dtypep()->arrayElements() * varp->widthWords(); + m_codeInc = varp->dtypep()->arrayElements() * varp->dtypep()->widthWords(); AstBasicDType* bdtypep = varp->basicp(); m_left = bdtypep ? bdtypep->left() : 0; m_right = bdtypep ? bdtypep->right() : 0; @@ -2555,8 +2637,8 @@ struct AstRand : public AstNodeTermop { private: bool m_reset; // Random reset, versus always random public: - AstRand(FileLine* fl, int wwidth, bool reset) : AstNodeTermop(fl) { - width(wwidth,wwidth); m_reset=reset; } + AstRand(FileLine* fl, AstNodeDType* dtp, bool reset) : AstNodeTermop(fl) { + dtypep(dtp); m_reset=reset; } AstRand(FileLine* fl) : AstNodeTermop(fl), m_reset(false) { } ASTNODE_NODE_FUNCS(Rand, RAND) virtual string emitVerilog() { return "%f$random"; } @@ -2891,12 +2973,13 @@ struct AstCast : public AstNode { } ASTNODE_NODE_FUNCS(Cast, CAST) virtual bool hasDType() const { return true; } - virtual string emitVerilog() { return "((%r)'(%l))"; } + virtual string emitVerilog() { return "((%d)'(%l))"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { V3ERROR_NA; return true;} virtual bool cleanLhs() {return true;} virtual bool sizeMattersLhs() {return false;} AstNode* lhsp() const { return op1p(); } - AstNodeDType* dtypep() const { return op2p()->castNodeDType(); } + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return op2p()->castNodeDType(); } }; struct AstCCast : public AstNodeUniop { @@ -3393,7 +3476,7 @@ struct AstShiftR : public AstNodeBiop { struct AstShiftRS : public AstNodeBiop { AstShiftRS(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0) : AstNodeBiop(fl, lhsp, rhsp) { - if (setwidth) { width(setwidth,setwidth); } + if (setwidth) { dtypeSetLogicSized(setwidth,setwidth,AstNumeric::SIGNED); } } ASTNODE_NODE_FUNCS(ShiftRS, SHIFTRS) virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftRS(lhs,rhs); } @@ -3652,7 +3735,11 @@ struct AstNeqWild : public AstNodeBiop { struct AstConcat : public AstNodeBiop { // If you're looking for {#{}}, see AstReplicate AstConcat(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - if (lhsp->width() && rhsp->width()) width(lhsp->width()+rhsp->width(),lhsp->width()+rhsp->width()); + if (lhsp->dtypep() && rhsp->dtypep()) { + dtypeSetLogicSized(lhsp->dtypep()->width()+rhsp->dtypep()->width(), + lhsp->dtypep()->width()+rhsp->dtypep()->width(), + AstNumeric::UNSIGNED); + } } ASTNODE_NODE_FUNCS(Concat, CONCAT) virtual string emitVerilog() { return "%f{%l, %k%r}"; } @@ -3664,7 +3751,11 @@ struct AstConcat : public AstNodeBiop { virtual int instrCount() const { return widthInstrs()*2; } }; struct AstReplicate : public AstNodeBiop { - AstReplicate(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {} + AstReplicate(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (AstConst* constp=rhsp->castConst()) { + dtypeSetLogicSized(lhsp->width()*constp->toUInt(), lhsp->width()*constp->toUInt(), AstNumeric::UNSIGNED); + } + } AstReplicate(FileLine* fl, AstNode* lhsp, uint32_t repCount) : AstNodeBiop(fl, lhsp, new AstConst(fl, repCount)) {} ASTNODE_NODE_FUNCS(Replicate, REPLICATE) @@ -4173,6 +4264,36 @@ struct AstCStmt : public AstNodeStmt { AstNode* bodysp() const { return op1p()->castNode(); } // op1= expressions to print }; +//###################################################################### +// Right below top + +struct AstTypeTable : public AstNode { + // Container for hash of standard data types + // Children: NODEDTYPEs + typedef map,AstBasicDType*> LogicMap; + AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX]; + // + enum { IDX0_LOGIC, IDX0_BIT, _IDX0_MAX }; + LogicMap m_logicMap[_IDX0_MAX][AstNumeric::_ENUM_MAX]; // uses above IDX enums + // + typedef map DetailedMap; + DetailedMap m_detailedMap; +public: + AstTypeTable(FileLine* fl) : AstNode(fl) { + for (int i=0; icastNodeDType();} // op1 = List of dtypes + void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); } + AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd); + AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, + int width, int widthMin, AstNumeric numeric); + AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + void clearCache(); + void repairCache(); + virtual void dump(ostream& str=cout); +}; + //###################################################################### // Top @@ -4180,13 +4301,22 @@ struct AstNetlist : public AstNode { // All modules are under this single top node. // Parents: none // Children: MODULEs & CFILEs - AstNetlist() : AstNode(new FileLine("AstRoot",0)) {} +private: + AstTypeTable* m_typeTablep; // Reference to top type table, for faster lookup +public: + AstNetlist() : AstNode(new FileLine("AstRoot",0)) { + m_typeTablep = NULL; + } ASTNODE_NODE_FUNCS(Netlist, NETLIST) AstNodeModule* modulesp() const { return op1p()->castNodeModule();} // op1 = List of modules AstNodeModule* topModulep() const { return op1p()->castNodeModule(); } // * = Top module in hierarchy (first one added, for now) void addModulep(AstNodeModule* modulep) { addOp1p(modulep); } AstCFile* filesp() const { return op2p()->castCFile();} // op2 = List of files void addFilesp(AstCFile* filep) { addOp2p(filep); } + AstNode* miscsp() const { return op3p();} // op3 = List of dtypes etc + void addMiscsp(AstNode* nodep) { addOp3p(nodep); } + AstTypeTable* typeTablep() { return m_typeTablep; } + void addTypeTablep(AstTypeTable* nodep) { m_typeTablep = nodep; addMiscsp(nodep); } }; //###################################################################### diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index d501eab2b..e3b1a7061 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -207,9 +207,22 @@ private: if (nodep->broken()) { nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)"); } + if (nodep->dtypep()) { + if (!nodep->dtypep()->brokeExists()) { nodep->v3error("Broken link in node->dtypep()"); } + else if (!nodep->dtypep()->castNodeDType()) { nodep->v3error("Non-dtype link in node->dtypep()"); } + } if (v3Global.assertDTypesResolved()) { - if (!nodep->width() && nodep->castNodeMath()) { - nodep->v3fatalSrc("Math node has no assigned width"); + if (nodep->hasDType()) { + if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype on node with hasDType(): "<prettyTypeName()); + } else { + if (nodep->dtypep()) nodep->v3fatalSrc("DType on node without hasDType(): "<prettyTypeName()); + } + if (nodep->getChildDTypep()) nodep->v3fatalSrc("childDTypep() non-null on node after should have removed"); + if (AstNodeDType* dnodep = nodep->castNodeDType()) { + if (dnodep->width() != dnodep->widthMin() + && v3Global.assertWidthsMatch()) { + dnodep->v3fatalSrc("Width != WidthMin"); + } } } if (v3Global.assertWidthsMatch()) { diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 59881ceeb..dfd085ca9 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -49,8 +49,10 @@ private: // Entire netlist: // AstNode::user() -> CleanState. For this node, 0==UNKNOWN // AstNode::user2() -> bool. True indicates widthMin has been propagated + // AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; // TYPES enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY }; @@ -71,16 +73,28 @@ private: else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE; else return nodep->widthWords()*VL_WORDSIZE; } - void setCppWidth (AstNode* nodep, int width, int widthMin) { + void setCppWidth (AstNode* nodep) { nodep->user2(true); // Don't resize it again - nodep->width(width, widthMin); + AstNodeDType* old_dtypep = nodep->dtypep(); + int width=cppWidth(nodep); // widthMin is unchanged + if (old_dtypep->width() != width) { + // Since any given dtype's cppWidth() is the same, we can just + // remember one convertion for each, and reuse it + if (AstNodeDType* new_dtypep = old_dtypep->user3p()->castNode()->castNodeDType()) { + nodep->dtypep(new_dtypep); + } else { + nodep->dtypeChgWidth(width, nodep->widthMin()); + AstNodeDType* new_dtypep = nodep->dtypep(); + if (new_dtypep == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed"); + old_dtypep->user3p(new_dtypep); // Remember for next time + } + } } void computeCppWidth (AstNode* nodep) { - if (!nodep->user2()) { + if (!nodep->user2() && nodep->hasDType()) { if (nodep->castVar() || nodep->castNodeDType()) { // Don't want to change variable widths! - setCppWidth(nodep, nodep->width(), nodep->width()); // set widthMin anyways so can see it later } else { - setCppWidth(nodep, cppWidth(nodep), nodep->widthMin()); + setCppWidth(nodep); } } } @@ -117,7 +131,7 @@ private: AstNode* cleanp = new AstAnd (nodep->fileline(), new AstConst (nodep->fileline(), mask), nodep); - setCppWidth (cleanp, cppWidth(nodep), nodep->widthMin()); + cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS relinkHandle.relink(cleanp); } void insureClean(AstNode* nodep) { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 3fa3b9663..73f0db652 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -858,16 +858,14 @@ private: // Form ranges AstSel* sel1p = new AstSel(conp->fileline(), rhsp, lsb1, msb1-lsb1+1); AstSel* sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2-lsb2+1); - sel1p->width(msb1-lsb1+1,msb1-lsb1+1); - sel2p->width(msb2-lsb2+1,msb2-lsb2+1); // Make new assigns of same flavor as old one //*** Not cloneTree; just one node. AstNode* newp = NULL; if (!need_temp) { AstNodeAssign* asn1ap=nodep->cloneType(lc1p, sel1p)->castNodeAssign(); AstNodeAssign* asn2ap=nodep->cloneType(lc2p, sel2p)->castNodeAssign(); - asn1ap->width(msb1-lsb1+1,msb1-lsb1+1); - asn2ap->width(msb2-lsb2+1,msb2-lsb2+1); + asn1ap->dtypeFrom(sel1p); + asn2ap->dtypeFrom(sel2p); // cppcheck-suppress nullPointer // addNext deals with it newp = newp->addNext(asn1ap); // cppcheck-suppress nullPointer // addNext deals with it @@ -896,10 +894,10 @@ private: AstNodeAssign* asn2bp=nodep->cloneType (lc2p, new AstVarRef(sel2p->fileline(), temp2p, false)) ->castNodeAssign(); - asn1ap->width(msb1-lsb1+1,msb1-lsb1+1); - asn1bp->width(msb1-lsb1+1,msb1-lsb1+1); - asn2ap->width(msb2-lsb2+1,msb2-lsb2+1); - asn2bp->width(msb2-lsb2+1,msb2-lsb2+1); + asn1ap->dtypeFrom(temp1p); + asn1bp->dtypeFrom(temp1p); + asn2ap->dtypeFrom(temp2p); + asn2bp->dtypeFrom(temp2p); // This order matters // cppcheck-suppress nullPointer // addNext deals with it newp = newp->addNext(asn1ap); @@ -945,7 +943,7 @@ private: AstAnd* newp = new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(), val), fromp); - newp->width(nodep->width(), nodep->width()); // widthMin no longer applicable + newp->dtypeSetLogicSized(nodep->width(), nodep->width(), AstNumeric::UNSIGNED); // widthMin no longer applicable if different C-expanded width nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; if (debug()>=9) newp->dumpTree(cout," _new: "); @@ -1096,8 +1094,8 @@ private: // expression, not the potentially smaller lsb1p's width newlsbp = new AstAdd(lsb1p->fileline(), lsb2p, new AstExtend(lsb1p->fileline(), lsb1p)); - newlsbp->widthFrom(lsb2p); // Unsigned - newlsbp->castAdd()->rhsp()->widthFrom(lsb2p); // Unsigned + newlsbp->dtypeFrom(lsb2p); // Unsigned + newlsbp->castAdd()->rhsp()->dtypeFrom(lsb2p); } AstSel* newp = new AstSel(nodep->fileline(), fromp, @@ -1240,16 +1238,6 @@ private: // Not constant propagated (for today) because AstMath::isOpaque is set // Someday if lower is constant, convert to quoted "string". - virtual void visit(AstAttrOf* nodep, AstNUser*) { - // Don't iterate children, don't want to lose VarRef. - if (nodep->attrType()==AstAttrType::EXPR_BITS) { - if (!nodep->fromp() || !nodep->fromp()->widthMin()) nodep->v3fatalSrc("Unsized expression"); - V3Number num (nodep->fileline(), 32, nodep->fromp()->widthMin()); - replaceNum(nodep, num); nodep=NULL; - } else { - nodep->v3fatalSrc("Missing ATTR type case"); - } - } bool onlySenItemInSenTree(AstNodeSenItem* nodep) { // Only one if it's not in a list return (!nodep->nextp() && nodep->backp()->nextp() != nodep); diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index d6b904100..4cd36e625 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -74,6 +74,7 @@ private: // AstNodeModule::user1() -> int. Count of number of cells referencing this module. // AstVar::user1() -> int. Count of number of references // AstVarScope::user1() -> int. Count of number of references + // AstNodeDType::user1() -> int. Count of number of references AstUser1InUse m_inuser1; // TYPES @@ -84,6 +85,7 @@ private: vector m_vscsp; // List of all encountered to avoid another loop through tree AssignMap m_assignMap; // List of all simple assignments for each variable bool m_elimUserVars; // Allow removal of user's vars + bool m_elimDTypes; // Allow removal of DTypes bool m_sideEffect; // Side effects discovered in assign RHS // METHODS @@ -92,7 +94,19 @@ private: if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } + void checkAll(AstNode* nodep) { + if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves + if (AstNode* subnodep = nodep->dtypep()) subnodep->user1Inc(); + } + if (AstNode* subnodep = nodep->getChildDTypep()) subnodep->user1Inc(); + } + void checkDType(AstNodeDType* nodep) { + if (!nodep->generic() // Don't remove generic types + && m_elimDTypes) { // dtypes stick around until post-widthing + m_varEtcsp.push_back(nodep); + } + if (AstNode* subnodep = nodep->virtRefDTypep()) subnodep->user1Inc(); } // VISITORS @@ -124,11 +138,17 @@ private: } virtual void visit(AstRefDType* nodep, AstNUser*) { nodep->iterateChildren(*this); + checkDType(nodep); checkAll(nodep); if (nodep->packagep()) { nodep->packagep()->user1Inc(); } } + virtual void visit(AstNodeDType* nodep, AstNUser*) { + nodep->iterateChildren(*this); + checkDType(nodep); + checkAll(nodep); + } virtual void visit(AstEnumItemRef* nodep, AstNUser*) { nodep->iterateChildren(*this); checkAll(nodep); @@ -156,6 +176,7 @@ private: // Similar code in V3Life m_sideEffect = false; nodep->rhsp()->iterateAndNext(*this); + checkAll(nodep); // Has to be direct assignment without any EXTRACTing. AstVarRef* varrefp = nodep->lhsp()->castVarRef(); if (varrefp && !m_sideEffect @@ -226,14 +247,19 @@ private: public: // CONSTRUCTORS - DeadVisitor(AstNetlist* nodep, bool elimUserVars) { + DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes) { m_elimUserVars = elimUserVars; + m_elimDTypes = elimDTypes; m_sideEffect = false; + // Prepare to remove some datatypes + nodep->typeTablep()->clearCache(); // Operate on whole netlist nodep->accept(*this); deadCheckVar(); // Modules after vars, because might be vars we delete inside a mod we delete deadCheckMod(); + // We may have removed some datatypes, cleanup + nodep->typeTablep()->repairCache(); } virtual ~DeadVisitor() {} }; @@ -243,13 +269,13 @@ public: void V3Dead::deadifyModules(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<iterateAndNext(*this); break; } + case 'd': { + if (!nodep->dtypep()) { nodep->v3fatalSrc("emitVerilog() references undef node"); } + else nodep->dtypep()->iterateAndNext(*this); + break; + } default: nodep->v3fatalSrc("Unknown emitVerilog format code: %"<fileline(), word)); } else if (nodep->isQuad() && word==0) { AstNode* quadfromp = nodep->cloneTree(true); - quadfromp->width(VL_QUADSIZE,quadfromp->widthMin()); + quadfromp->dtypeSetBitSized(VL_QUADSIZE,quadfromp->widthMin(),AstNumeric::UNSIGNED); return new AstCCast (nodep->fileline(), quadfromp, VL_WORDSIZE); } else if (nodep->isQuad() && word==1) { AstNode* quadfromp = nodep->cloneTree(true); - quadfromp->width(VL_QUADSIZE,quadfromp->widthMin()); + quadfromp->dtypeSetBitSized(VL_QUADSIZE,quadfromp->widthMin(),AstNumeric::UNSIGNED); return new AstCCast (nodep->fileline(), new AstShiftR (nodep->fileline(), quadfromp, @@ -439,7 +439,7 @@ private: AstNode* newp = lowp; if (midp) newp = new AstOr (nodep->fileline(), midp, newp); if (hip) newp = new AstOr (nodep->fileline(), hip, newp); - newp->widthFrom(nodep); + newp->dtypeFrom(nodep); replaceWithDelete(nodep,newp); nodep=NULL; } else { // Long/Quad from Long/Quad @@ -664,7 +664,7 @@ private: new AstConst (nodep->fileline(), rhsshift), nodep->width()), rhsp); - newp->widthFrom(nodep); // Unsigned + newp->dtypeFrom(nodep); // Unsigned replaceWithDelete(nodep,newp); nodep=NULL; } } @@ -713,11 +713,11 @@ private: new AstConst (nodep->fileline(), rhsshift), nodep->width()), newp); - newp->widthFrom(nodep); // Unsigned + newp->dtypeFrom(nodep); // Unsigned } lhsp->deleteTree(); // Never used } - newp->widthFrom(nodep); // Unsigned + newp->dtypeFrom(nodep); // Unsigned replaceWithDelete(nodep,newp); nodep=NULL; } } @@ -732,7 +732,7 @@ private: AstNode* newp; if (lhswidth==1) { newp = new AstNegate (nodep->fileline(), lhsp->cloneTree(true)); - newp->width(VL_WORDSIZE,VL_WORDSIZE); + newp->dtypeSetLogicSized(VL_WORDSIZE,VL_WORDSIZE,AstNumeric::UNSIGNED); // Replicate always unsigned } else { newp = newAstWordSelClone (lhsp, w); for (unsigned repnum=1; repnumv3fatalSrc("sameHash function undefined (returns 0) for node under CFunc."); } - m_lowerHash = V3Hash(m_lowerHash, V3Hash(nodep->type()<<6 | nodep->width())); + // For identical nodes, the type should be the same thus dtypep should be the same too + m_lowerHash = V3Hash(m_lowerHash, V3Hash(nodep->type()<<6, V3Hash(nodep->dtypep()))); // Now update m_lowerHash for our children's (and next children) contributions nodep->iterateChildren(*this); // Store the hash value diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index d8eb60c4b..272a9a494 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -90,13 +90,12 @@ private: V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, false); // Make a ASSIGNW (expr, pin) AstNode* exprp = nodep->exprp()->cloneTree(false); - if (nodep->width() != nodep->modVarp()->width()) + if (exprp->width() != nodep->modVarp()->width()) nodep->v3fatalSrc("Width mismatch, should have been handled in pinReconnectSimple\n"); if (nodep->modVarp()->isInout()) { nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator"); } else if (nodep->modVarp()->isOutput()) { AstNode* rhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false); - rhsp->widthSignedFrom(nodep); AstAssignW* assp = new AstAssignW (exprp->fileline(), exprp, rhsp); m_modp->addStmtp(assp); } else if (nodep->modVarp()->isInput()) { @@ -258,7 +257,7 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModu && connBasicp->width() == pinBasicp->width() && connBasicp->lsb() == pinBasicp->lsb() && !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types - && pinp->width() == pinVarp->width() + && connBasicp->width() == pinVarp->width() && 1) { // Done. One to one interconnect won't need a temporary variable. } else if (!forTristate && pinp->exprp()->castConst()) { @@ -276,16 +275,16 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModu } else if (pinVarp->isOutput()) { // See also V3Inst AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, false); - if (pinp->width() > rhsp->width()) { + if (pinVarp->width() > rhsp->width()) { if (rhsp->isSigned()) { rhsp = new AstExtendS(pinp->fileline(), rhsp); } else { rhsp = new AstExtend (pinp->fileline(), rhsp); } - } else if (pinp->width() < rhsp->width()) { - rhsp = new AstSel (pinp->fileline(), rhsp, 0, pinp->width()); + } else if (pinVarp->width() < rhsp->width()) { + rhsp = new AstSel (pinp->fileline(), rhsp, 0, pinVarp->width()); } - rhsp->widthSignedFrom(pinp); + rhsp->dtypeFrom(pinVarp); // Need proper widthMin, which may differ from AstSel created above assignp = new AstAssignW (pinp->fileline(), pinexprp, rhsp); pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, true)); } else { @@ -296,7 +295,6 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModu pinexprp); pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false)); } - pinp->widthSignedFrom(pinp->exprp()); if (assignp) cellp->addNextHere(assignp); //if (1||debug()) { pinp->dumpTree(cout," out:"); } //if (1||debug()) { assignp->dumpTree(cout," aout:"); } diff --git a/src/V3Link.cpp b/src/V3Link.cpp index 6dcb17650..2c0530679 100644 --- a/src/V3Link.cpp +++ b/src/V3Link.cpp @@ -433,7 +433,6 @@ private: else dtypep = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::LOGIC); AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::OUTPUT, nodep->name(), VFlagChildDType(), dtypep); // Not dtype resolved yet - if (nodep->isSigned()) newvarp->numeric(AstNumeric::SIGNED); newvarp->funcReturn(true); newvarp->trace(false); // Not user visible newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index ef1b323dc..1a1574b13 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -140,9 +140,7 @@ private: string name = string("__Vrepeat")+cvtToStr(m_repeatNum++); // Spec says value is integral, if negative is ignored AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name, - VFlagBitPacked(), 32); - varp->numeric(AstNumeric::SIGNED); - varp->dtypep()->numeric(AstNumeric::SIGNED); + nodep->findBasicDType(nodep->fileline(), AstBasicDTypeKwd::INTEGER)); varp->usedLoopIdx(true); m_modp->addStmtp(varp); AstNode* initsp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true), diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 02005bda0..114c1e71a 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -46,7 +46,6 @@ private: // STATE bool m_setRefLvalue; // Set VarRefs to lvalues for pin assignments - AstInitial* m_initialp; // In an initial block AstNodeFTask* m_ftaskp; // Function or task we're inside // METHODS @@ -69,10 +68,6 @@ private: nodep->v3warn(ASSIGNIN,"Assigning to input variable: "<prettyName()); } } - if (nodep->lvalue() && nodep->varp()->isConst() - && !m_initialp) { // Too loose, but need to allow our generated first assignment - nodep->v3error("Assigning to const variable: "<prettyName()); - } } nodep->iterateChildren(*this); } @@ -228,11 +223,6 @@ private: } m_setRefLvalue = last_setRefLvalue; } - virtual void visit(AstInitial* nodep, AstNUser*) { - m_initialp = nodep; - nodep->iterateChildren(*this); - m_initialp = NULL; - } virtual void visit(AstNodeFTask* nodep, AstNUser*) { m_ftaskp = nodep; nodep->iterateChildren(*this); @@ -270,7 +260,6 @@ public: LinkLValueVisitor(AstNode* nodep, bool start) { m_setRefLvalue = start; m_ftaskp = NULL; - m_initialp = NULL; nodep->accept(*this); } virtual ~LinkLValueVisitor() {} diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index bdaa9d28e..73b1d268e 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -124,7 +124,6 @@ void V3LinkLevel::wrapTop(AstNetlist* netlistp) { varp, oldvarp->isOutput())); // Skip length and width comp; we know it's a direct assignment pinp->modVarp(oldvarp); - pinp->widthSignedFrom(oldvarp); cellp->addPinsp(pinp); } } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index f588757a1..1ff5d422b 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -368,7 +368,7 @@ private: else if (backp->castNodeFTask()) break; } if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type"); - AstNodeDType* dtypep = nodep->dtypep(); dtypep->unlinkFrBack(); + AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack(); if (backp->castTypedef()) { // A typedef doesn't need us to make yet another level of typedefing // For typedefs just remove the AstRefDType level of abstraction nodep->replaceWith(dtypep); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 1245475e2..879951dc3 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -92,13 +92,6 @@ private: virtual void visit(AstVar* nodep, AstNUser*) { nodep->iterateChildren(*this); - if (nodep->isIO() && !(nodep->dtypeSkipRefp()->castBasicDType() || - nodep->dtypeSkipRefp()->castArrayDType())) { - nodep->v3error("Unsupported: Inputs and outputs must be simple data types"); - } - if (nodep->dtypeSkipRefp()->castConstDType()) { - nodep->isConst(true); - } if (m_ftaskp) nodep->funcLocal(true); if (nodep->isSigModPublic()) { nodep->sigModPublic(false); // We're done with this attribute diff --git a/src/V3Param.cpp b/src/V3Param.cpp index ae13d4298..e521525a7 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -323,7 +323,7 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) { if (!constp) { //if (debug()) pinp->dumpTree(cout,"error:"); pinp->v3error("Can't convert defparam value to constant: Param "<name()<<" of "<prettyName()); - pinp->exprp()->replaceWith(new AstConst(pinp->fileline(), V3Number(pinp->fileline(), pinp->width(), 0))); + pinp->exprp()->replaceWith(new AstConst(pinp->fileline(), V3Number(pinp->fileline(), modvarp->width(), 0))); } else if (origconstp && constp->sameTree(origconstp)) { // Setting parameter to its default value. Just ignore it. // This prevents making additional modules, and makes coverage more diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 2997f8bb0..8b9645717 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -220,10 +220,11 @@ private: V3Number zeronum (nodep->fileline(), nodep->width(), 0); constzerop = new AstConst(nodep->fileline(), zeronum); } - constzerop->widthFrom (nodep); // unsigned - V3Number widthnum (nodep->fileline(), nodep->rhsp()->widthMin(), nodep->width()-1); + constzerop->dtypeFrom (nodep); // unsigned + + V3Number widthnum (nodep->fileline(), nodep->rhsp()->widthMin(), m1value); AstNode* constwidthp = new AstConst(nodep->fileline(), widthnum); - constwidthp->widthFrom (nodep->rhsp()); // unsigned + constwidthp->dtypeFrom (nodep->rhsp()); // unsigned AstCond* newp = new AstCond (nodep->fileline(), new AstLte (nodep->fileline(), diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 2265b1e8d..2a90c6ec1 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -280,6 +280,9 @@ class SliceVisitor : public AstNVisitor { } UINFO(9," ArraySel-child: "<fileline(), topp, new AstConst(nodep->fileline(),lsb)); + if (!newp->dtypep()) { + newp->v3fatalSrc("ArraySel dtyping failed when resolving slice"); // see ArraySel constructor + } newp->user1p(refp); newp->start(lsb); newp->length(msb - lsb + 1); diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index 7151a49f5..5e9be0c18 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -93,7 +93,7 @@ private: virtual void visit(AstVar* nodep, AstNUser*) { allNodes(nodep); nodep->iterateChildren(*this); - if (m_counting) { + if (m_counting && nodep->dtypep()) { if (nodep->isUsedClock()) ++m_statVarClock; if (nodep->dtypeSkipRefp()->castArrayDType()) ++m_statVarArray; else m_statVarBytes += nodep->dtypeSkipRefp()->widthTotalBytes(); diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 1e2e91936..ece2ccc87 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -192,8 +192,10 @@ private: FileLine* fl = nodep->fileline(); AstNodeDType* dtypep = new AstArrayDType (fl, - new AstBasicDType(fl, VFlagBitPacked(), m_outVarps.size()), - new AstRange (fl, VL_MASK_I(m_inWidth), 0)); + nodep->findBitDType(nodep->fileline(), m_outVarps.size(), + m_outVarps.size(), AstNumeric::UNSIGNED), + new AstRange (fl, VL_MASK_I(m_inWidth), 0), false); + v3Global.rootp()->typeTablep()->addTypesp(dtypep); AstVar* chgVarp = new AstVar (fl, AstVarType::MODULETEMP, "__Vtablechg" + cvtToStr(m_modTables), @@ -237,10 +239,9 @@ private: AstVar* outvarp = outvscp->varp(); FileLine* fl = nodep->fileline(); AstNodeDType* dtypep - = new AstArrayDType (fl, - // FUTURE: If support more types, below can use outvarp->dtype() - new AstBasicDType(fl, VFlagLogicPacked(), outvarp->width()), - new AstRange (fl, VL_MASK_I(m_inWidth), 0)); + = new AstArrayDType (fl, outvarp->dtypep(), + new AstRange (fl, VL_MASK_I(m_inWidth), 0), false); + v3Global.rootp()->typeTablep()->addTypesp(dtypep); AstVar* tablevarp = new AstVar (fl, AstVarType::MODULETEMP, "__Vtable" + cvtToStr(m_modTables) +"_"+outvarp->name(), diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 9b95a902b..a0f3853ff 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -336,7 +336,7 @@ private: } AstVarScope* createInputVar(AstCFunc* funcp, const string& name, AstBasicDTypeKwd kwd) { AstVar* newvarp = new AstVar (funcp->fileline(), AstVarType::BLOCKTEMP, name, - new AstBasicDType(funcp->fileline(), kwd)); + funcp->findBasicDType(funcp->fileline(), kwd)); newvarp->funcLocal(true); newvarp->combineType(AstVarType::INPUT); funcp->addArgsp(newvarp); diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 153b75e7a..2ed6d23b9 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -161,11 +161,11 @@ class TristateVisitor : public TristateBaseVisitor { return invarp->user4p()->castNode()->castVar(); } - AstVar* getCreateUnconnVarp(AstNode* fromp) { + AstVar* getCreateUnconnVarp(AstNode* fromp, AstNodeDType* dtypep) { AstVar* newp = new AstVar(fromp->fileline(), AstVarType::MODULETEMP, "__Vtriunconn"+cvtToStr(m_unique++), - VFlagLogicPacked(), fromp->width()); + dtypep); if (!m_modp) { newp->v3error("Unsupported: Creating tristate signal not underneath a module"); } else m_modp->addStmtp(newp); return newp; @@ -211,7 +211,7 @@ class TristateVisitor : public TristateBaseVisitor { // A pin with 1'b0 or similar connection results in an assign with constant on LHS // due to the pinReconnectSimple call in visit AstPin. // We can ignore the output override by making a temporary - AstVar* varp = getCreateUnconnVarp(nodep); + AstVar* varp = getCreateUnconnVarp(nodep, nodep->dtypep()); AstNode* newp = new AstVarRef(nodep->fileline(), varp, true); UINFO(9," const->"<replaceWith(newp); @@ -524,7 +524,7 @@ class TristateVisitor : public TristateBaseVisitor { if (!nodep->exprp()) { // No-connect; covert to empty connection UINFO(5,"Unconnected pin terminate "<modVarp()->dtypep()); nodep->exprp(new AstVarRef(nodep->fileline(), ucVarp, nodep->modVarp()->isOutput())); // We don't need a driver on the wire; the lack of one will default to tristate @@ -541,7 +541,6 @@ class TristateVisitor : public TristateBaseVisitor { nodep->pinNum(), nodep->name() + "__en" + cvtToStr(m_unique++), new AstVarRef(nodep->fileline(), enVarp, true)); - enpinp->widthSignedFrom(enModVarp); enpinp->modVarp(enModVarp); enpinp->user2(true); // mark this visited m_cellp->addPinsp(enpinp); @@ -559,7 +558,6 @@ class TristateVisitor : public TristateBaseVisitor { nodep->pinNum(), nodep->name() + "__out"+cvtToStr(m_unique), outexprp); - outpinp->widthSignedFrom(outModVarp); outpinp->modVarp(outModVarp); outpinp->user2(true); // mark this visited m_cellp->addPinsp(outpinp); diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 0bbd1eed4..00d1c77cc 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -306,7 +306,7 @@ private: new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(),numbx), new AstRand(nodep->fileline(), - nodep->width(), true))))); + nodep->dtypep(), true))))); // Add inits in front of other statement. // In the future, we should stuff the initp into the module's constructor. AstNode* afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 28b9c4877..87f6b0970 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -91,11 +91,16 @@ class WidthVP : public AstNUser { // Parameters to pass down hierarchy with visit functions. int m_width; // Expression width, for (2+2), it's 32 bits int m_widthMin; // Minimum width, for (2+2), it's 2 bits, for 32'2+32'2 it's 32 bits + AstNodeDType* m_dtypep; // Parent's data type to resolve to Stage m_stage; // If true, report errors public: - WidthVP(int width, int widthMin, Stage stage) : m_width(width), m_widthMin(widthMin), m_stage(stage) {} + WidthVP(int width, int widthMin, Stage stage) + : m_width(width), m_widthMin(widthMin), m_dtypep(NULL), m_stage(stage) {} + WidthVP(AstNodeDType* dtypep, Stage stage) + : m_width(0), m_widthMin(0), m_dtypep(dtypep), m_stage(stage) {} int width() const { return m_width; } int widthMin() const { return m_widthMin?m_widthMin:m_width; } + AstNodeDType* dtypep() const { return m_dtypep; } bool prelim() const { return m_stage&1; } bool final() const { return m_stage&2; } char stageAscii() const { return "-PFB"[m_stage]; } @@ -114,6 +119,7 @@ private: AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations AstNodeCase* m_casep; // Current case statement CaseItem is under AstFunc* m_funcp; // Current function + AstInitial* m_initialp; // Current initial block bool m_doGenerate; // Do errors later inside generate statement // CLASSES @@ -326,7 +332,7 @@ private: } } if (vup->c()->final()) { - if (!nodep->widthSized()) { + if (!nodep->dtypep()->widthSized()) { // See also error in V3Number nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in concatenations."); } @@ -350,7 +356,7 @@ private: AstNumeric::UNSIGNED); } if (vup->c()->final()) { - if (!nodep->widthSized()) { + if (!nodep->dtypep()->widthSized()) { // See also error in V3Number nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in replications."); } @@ -486,14 +492,12 @@ private: int fromlsb; AstNodeDType* ddtypep = varrp->varp()->dtypep()->dtypeDimensionp(dimension); if (AstArrayDType* adtypep = ddtypep->castArrayDType()) { - int outwidth = varrp->width(); // Width of variable frommsb = adtypep->msb(); fromlsb = adtypep->lsb(); if (fromlsb>frommsb) {int t=frommsb; frommsb=fromlsb; fromlsb=t; } // However, if the lsb<0 we may go negative, so need more bits! if (fromlsb < 0) frommsb += -fromlsb; - nodep->numericFrom(adtypep); - nodep->width(outwidth,outwidth); // Width out = width of array + nodep->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference } else { nodep->v3fatalSrc("Array reference exceeds dimension of array"); @@ -551,9 +555,9 @@ private: // the number's sign. So we don't: nodep->dtypeChgSigned(nodep->num().isSigned()); if (vup && vup->c()->prelim()) { if (nodep->num().sized()) { - nodep->width(nodep->num().width(), nodep->num().width()); + nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width()); } else { - nodep->width(nodep->num().width(), nodep->num().widthMin()); + nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthMin()); } } // We don't size the constant until we commit the widths, as need parameters @@ -626,64 +630,94 @@ private: } virtual void visit(AstAttrOf* nodep, AstNUser*) { nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - nodep->numeric(AstNumeric::UNSIGNED); - nodep->width(32,1); // Approximation, unsized 32 + // Don't iterate children, don't want to lose VarRef. + if (nodep->attrType()==AstAttrType::EXPR_BITS) { + if (!nodep->fromp() || !nodep->fromp()->widthMin()) nodep->v3fatalSrc("Unsized expression"); + V3Number num (nodep->fileline(), 32, nodep->fromp()->width()); + nodep->replaceWith(new AstConst(nodep->fileline(), num)); nodep->deleteTree(); nodep=NULL; + } else { // Everything else resolved earlier + nodep->dtypeSetLogicSized(32,1,AstNumeric::UNSIGNED); // Approximation, unsized 32 + nodep->v3fatalSrc("Missing ATTR type case"); + } } virtual void visit(AstText* nodep, AstNUser* vup) { // Only used in CStmts which don't care.... } - virtual void visit(AstArrayDType* nodep, AstNUser* vup) { - // Lower datatype determines the width - nodep->dtypep()->iterateAndNext(*this,vup); - // But also cleanup array size + + // DTYPES + virtual void visit(AstArrayDType* nodep, AstNUser*) { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + // Cleanup array size nodep->rangep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - nodep->widthFrom(nodep->dtypep()); - nodep->numericFrom(nodep->dtypep()); + nodep->dtypep(nodep); // The array itself, not subDtype + nodep->widthFromSub(nodep->subDTypep()); UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->generic()) return; // Already perfect if (nodep->rangep()) { nodep->rangep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - nodep->width(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst()); + // Because this DType has a unique child range, we know it's not pointed at by + // other nodes unless they are referencing this type. Furthermore the width() + // calculation would return identical values. Therefore we can directly replace the width + nodep->widthForce(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst()); } // else width in node is correct; it was set based on keyword().width() // at construction time. Ditto signed, so "unsigned byte" etc works right. + nodep->cvtRangeConst(); + // TODO: If BasicDType now looks like a generic type, we can convert to a real generic dtype + // Instead for now doing this in V3WidthCommit UINFO(4,"dtWidthed "<iterateChildren(*this, vup); - nodep->widthFrom(nodep->dtypep()); + virtual void visit(AstConstDType* nodep, AstNUser*) { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + // Move any childDTypep to instead be in global AstTypeTable. + // This way if this node gets deleted and some other dtype points to it + // it won't become a dead pointer. This doesn't change the object pointed to. + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->iterateChildren(*this); + nodep->dtypep(nodep); // Should already be set, but be clear it's not the subDType + nodep->widthFromSub(nodep->subDTypep()); UINFO(4,"dtWidthed "<iterateChildren(*this, vup); - if (nodep->defp()) nodep->defp()->iterate(*this,vup); - nodep->widthSignedFrom(nodep->dtypeSkipRefp()); + virtual void visit(AstRefDType* nodep, AstNUser*) { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + nodep->iterateChildren(*this); + if (nodep->subDTypep()) nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->dtypeFrom(nodep->dtypeSkipRefp()); + nodep->widthFromSub(nodep->subDTypep()); UINFO(4,"dtWidthed "<iterateChildren(*this, vup); - nodep->widthSignedFrom(nodep->dtypep()->skipRefp()); + virtual void visit(AstTypedef* nodep, AstNUser*) { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); + nodep->iterateChildren(*this); + nodep->dtypep(iterateEditDTypep(nodep, nodep->subDTypep())); } virtual void visit(AstCast* nodep, AstNUser* vup) { + if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); + nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep())); //if (debug()) nodep->dumpTree(cout," CastPre: "); nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - nodep->dtypep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); // When more general casts are supported, the cast elimination will be done later. // For now, replace it ASAP, so widthing can propagate easily // The cast may change signing, but we don't know the sign yet. Make it so. // Note we don't sign lhsp() that would make the algorithm O(n^2) if lots of casting. - V3Width::widthParamsEdit(nodep->dtypep()); // MAY CHANGE dtypep() - AstBasicDType* basicp = nodep->dtypep()->basicp(); if (!basicp) nodep->v3fatalSrc("Unimplemented: Casting non-simple data type"); - nodep->widthSignedFrom(basicp); + AstBasicDType* basicp = nodep->dtypep()->basicp(); + if (!basicp) nodep->v3fatalSrc("Unimplemented: Casting non-simple data type"); + // When implement more complicated types need to convert childDTypep to dtypep() not as a child if (!basicp->isDouble() && !nodep->lhsp()->isDouble()) { // Note widthCheck might modify nodep->lhsp() widthCheck(nodep,"Cast",nodep->lhsp(),nodep->width(),nodep->width(),true); } AstNode* newp = nodep->lhsp()->unlinkFrBack(); - if (basicp->numeric() == newp->numeric()) { - newp = newp; // Can just remove cast - } else if (basicp->isDouble() && !newp->isDouble()) { + if (basicp->isDouble() && !newp->isDouble()) { newp = new AstIToRD(nodep->fileline(), newp); } else if (!basicp->isDouble() && newp->isDouble()) { if (basicp->isSigned()) { @@ -692,10 +726,12 @@ private: newp = new AstUnsigned(nodep->fileline(), new AstRToIS(nodep->fileline(), newp)); } - } else if (basicp->isSigned()) { + } else if (basicp->isSigned() && !newp->isSigned()) { newp = new AstSigned(nodep->fileline(), newp); - } else { + } else if (!basicp->isSigned() && newp->isSigned()) { newp = new AstUnsigned(nodep->fileline(), newp); + } else { + newp = newp; // Can just remove cast } nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; @@ -712,13 +748,25 @@ private: nodep->v3error("Variable's initial value is circular: "<prettyName()); pushDeletep(nodep->valuep()->unlinkFrBack()); nodep->valuep(new AstConst(nodep->fileline(), AstConst::LogicTrue())); - nodep->widthFrom(nodep->valuep()); + nodep->dtypeFrom(nodep->valuep()); nodep->didWidth(true); return; } nodep->doingWidth(true); + // Make sure dtype is sized + if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); + nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep())); + if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype determined for var"); + if (nodep->isIO() && !(nodep->dtypeSkipRefp()->castBasicDType() || + nodep->dtypeSkipRefp()->castArrayDType())) { + nodep->v3error("Unsupported: Inputs and outputs must be simple data types"); + } + if (nodep->dtypeSkipRefp()->castConstDType()) { + nodep->isConst(true); + } // Parameters if implicit untyped inherit from what they are assigned to AstBasicDType* bdtypep = nodep->dtypep()->castBasicDType(); + bool didchk = false; bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit(); if (implicitParam) { int width=0; @@ -731,49 +779,38 @@ private: // This prevents width warnings at the location the parameter is substituted in if (nodep->valuep()->isDouble()) { nodep->dtypeSetDouble(); bdtypep=NULL; - nodep->dtypep()->dtypeSetDouble(); bdtypep=NULL; nodep->valuep()->iterateAndNext(*this,WidthVP(width,0,FINAL).p()); } else { - nodep->valuep()->iterateAndNext(*this,WidthVP(width,0,FINAL).p()); - if (bdtypep->nosigned()) rs = nodep->valuep()->numeric(); - else rs = nodep->numeric(); - if (nodep->valuep()->widthSized()) { + AstBasicDType* valueBdtypep = nodep->valuep()->dtypep()->basicp(); + bool issigned = false; + if (bdtypep->isNosign()) { + if (valueBdtypep && valueBdtypep->isSigned()) issigned=true; + } else issigned = bdtypep->isSigned(); + if (nodep->valuep()->dtypep()->widthSized()) { width = nodep->valuep()->width(); } else { if (nodep->valuep()->width()>32) nodep->valuep()->v3warn(WIDTH,"Assigning >32 bit to unranged parameter (defaults to 32 bits)"); width = 32; } + // Can't just inherit valuep()->dtypep() as mwidth might not equal width + nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(),issigned); + didchk = true; + nodep->valuep()->iterateAndNext(*this,WidthVP(width,nodep->widthMin(),FINAL).p()); } UINFO(9,"implicitParamFromIV "<valuep()<isDouble()) { - AstBasicDType* newp = new AstBasicDType(nodep->fileline(), VFlagLogicPacked(), width); - newp->implicit(true); - newp->numeric(rs); // SIGNED or UNSIGNED - bdtypep->replaceWith(newp); - bdtypep->deleteTree(); bdtypep=NULL; - nodep->dtypep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + // Or, if nothing assigned, they're integral + nodep->dtypeSetSigned32(); bdtypep=NULL; } } - else { // non param or sized param - nodep->dtypep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - if (nodep->valuep()) { - nodep->valuep()->iterateAndNext(*this,WidthVP(nodep->dtypep()->width(),0,BOTH).p()); - //if (debug()) nodep->dumpTree(cout," final: "); - } + else if (bdtypep && bdtypep->implicit()) { // Implicits get converted to size 1 + nodep->dtypeSetLogicSized(1,1,bdtypep->numeric()); bdtypep=NULL; } - // See above note about valuep()->...FINAL - nodep->widthSignedFrom(nodep->dtypep()); if (nodep->valuep()) { - if (implicitParam && !nodep->isDouble()) { - nodep->width(nodep->width(), nodep->valuep()->widthMin()); // Needed as mwidth might not equal width - } - widthCheck(nodep,"Initial value",nodep->valuep(), - nodep->width(),nodep->widthMin()); + //if (debug()) nodep->dumpTree(cout," final: "); + if (!didchk) nodep->valuep()->iterateAndNext(*this,WidthVP(nodep->dtypep()->width(),0,BOTH).p()); + widthCheck(nodep,"Initial value",nodep->valuep(),nodep->width(),nodep->widthMin()); } UINFO(4,"varWidthed "<dumpTree(cout," InitOut: "); @@ -781,28 +818,36 @@ private: nodep->doingWidth(false); } virtual void visit(AstNodeVarRef* nodep, AstNUser* vup) { + if (nodep->didWidth()) return; if (!nodep->varp()->didWidth()) { // Var hasn't been widthed, so make it so. nodep->varp()->iterate(*this); } //if (debug()>=9) { nodep->dumpTree(cout," VRin "); nodep->varp()->dumpTree(cout," forvar "); } // Note genvar's are also entered as integers - nodep->numericFrom(nodep->varp()); + nodep->dtypeFrom(nodep->varp()); if (nodep->backp()->castNodeAssign() && nodep->lvalue()) { // On LHS - // Consider Integers on LHS to sized (else may inherit unsized.) - nodep->width(nodep->varp()->width(), nodep->varp()->width()); - } else { - nodep->widthFrom(nodep->varp()); + if (!nodep->widthMin()) v3fatalSrc("LHS var should be size complete"); } //if (debug()>=9) nodep->dumpTree(cout," VRout "); + if (nodep->lvalue() && nodep->varp()->isConst() + && !m_paramsOnly + && !m_initialp) { // Too loose, but need to allow our generated first assignment + // // Move this to a property of the AstInitial block + nodep->v3error("Assigning to const variable: "<prettyName()); + } + nodep->didWidth(true); } virtual void visit(AstEnumDType* nodep, AstNUser* vup) { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed UINFO(5," ENUMDTYPE "<dtypep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); - nodep->widthFrom(nodep->dtypep()); + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->dtypep(nodep); + nodep->widthFromSub(nodep->subDTypep()); // Assign widths - nodep->itemsp()->iterateAndNext(*this,WidthVP(nodep->width(),nodep->widthMin(),BOTH).p()); + nodep->itemsp()->iterateAndNext(*this,WidthVP(nodep->dtypep(),BOTH).p()); // Assign missing values V3Number num (nodep->fileline(), nodep->width(), 0); V3Number one (nodep->fileline(), nodep->width(), 1); @@ -814,12 +859,23 @@ private: if (!itemp->valuep()->castConst()) { itemp->valuep()->v3error("Enum value isn't a constant"); itemp->valuep()->unlinkFrBack()->deleteTree(); + continue; } + // TODO IEEE says assigning sized number that is not same size as enum is illegal + } + if (!itemp->valuep()) { + if (num.isEqZero() && itemp != nodep->itemsp()) + itemp->v3error("Enum value wrapped around"); // IEEE says illegal + if (!nodep->dtypep()->basicp() + && !nodep->dtypep()->basicp()->keyword().isIntNumeric()) { + itemp->v3error("Enum names without values only allowed on numeric types"); + // as can't +1 to resolve them. + } + itemp->valuep(new AstConst(itemp->fileline(), num)); } - if (!itemp->valuep()) itemp->valuep(new AstConst(itemp->fileline(), num)); num.opAssign(itemp->valuep()->castConst()->num()); // Look for duplicates - if (inits.find(num) != inits.end()) { + if (inits.find(num) != inits.end()) { // IEEE says illegal itemp->v3error("Overlapping enumeration value: "<prettyName()); inits.find(num)->second->v3error("... Location of original declaration"); } else { @@ -830,16 +886,18 @@ private: } virtual void visit(AstEnumItem* nodep, AstNUser* vup) { UINFO(5," ENUMITEM "<c()->width(); - int mwidth = vup->c()->widthMin(); - nodep->width(width, mwidth); - if (nodep->valuep()) { - nodep->valuep()->iterateAndNext(*this,WidthVP(width,mwidth,BOTH).p()); + AstNodeDType* vdtypep = vup->c()->dtypep(); + nodep->dtypep(vdtypep); + if (nodep->valuep()) { // else the value will be assigned sequentially + int width = vdtypep->width(); // Always from parent type + nodep->valuep()->iterateAndNext(*this,WidthVP(width,0,BOTH).p()); + int mwidth = nodep->valuep()->widthMin(); // Value determines minwidth + nodep->dtypeChgWidth(width, mwidth); widthCheck(nodep,"Enum value",nodep->valuep(),width,mwidth); } } virtual void visit(AstEnumItemRef* nodep, AstNUser* vup) { - if (nodep->itemp()->width()==0) { + if (!nodep->itemp()->didWidth()) { // We need to do the whole enum en-mass AstNode* enump = nodep->itemp(); if (!enump) nodep->v3fatalSrc("EnumItemRef not linked"); @@ -925,9 +983,8 @@ private: nodep->ifsp()->iterateAndNext(*this); nodep->elsesp()->iterateAndNext(*this); } - nodep->condp()->iterateAndNext(*this,WidthVP(1,1,PRELIM).p()); + nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); spliceCvtCmpD0(nodep->condp()); - nodep->condp()->iterateAndNext(*this,WidthVP(1,1,FINAL).p()); widthCheckReduce(nodep,"If",nodep->condp(),1,1); // it's like an if() condition. //if (debug()) nodep->dumpTree(cout," IfOut: "); } @@ -936,7 +993,8 @@ private: //if (debug()) nodep->dumpTree(cout," AssignPre: "); nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); - if (!nodep->lhsp()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?"); + if (!nodep->lhsp()->dtypep()) nodep->v3fatalSrc("How can LHS be untyped?"); + if (!nodep->lhsp()->dtypep()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?"); if (!nodep->lhsp()->isDouble() && nodep->rhsp()->isDouble()) { spliceCvtS(nodep->rhsp(), false); // Round RHS } else if (nodep->lhsp()->isDouble() && !nodep->rhsp()->isDouble()) { @@ -947,8 +1005,8 @@ private: awidth = nodep->rhsp()->width(); // Parameters can propagate by unsized assignment } nodep->rhsp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p()); - nodep->numericFrom(nodep->lhsp()); - nodep->width(awidth,awidth); // We know the assign will truncate, so rather + nodep->dtypeFrom(nodep->lhsp()); + nodep->dtypeChgWidth(awidth,awidth); // We know the assign will truncate, so rather // than using "width" and have the optimizer truncate the result, we do // it using the normal width reduction checks. //UINFO(0,"aw "<rhsp()->width()<<" m"<rhsp()->widthMin()<modVarp()->iterate(*this); } if (!nodep->exprp()) { // No-connect - nodep->widthSignedFrom(nodep->modVarp()); return; } // Very much like like an assignment, but which side is LH/RHS @@ -1151,7 +1208,6 @@ private: awidth = expwidth; } } - nodep->width(awidth,awidth); nodep->exprp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p()); if (!m_cellRangep) { widthCheckPin(nodep, nodep->exprp(), pinwidth, inputPin); @@ -1193,8 +1249,7 @@ private: if (nodep->fvarp()) { m_funcp = nodep->castFunc(); if (!m_funcp) nodep->v3fatalSrc("FTask with function variable, but isn't a function"); - nodep->numericFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() - nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width()); + nodep->dtypeFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() } nodep->didWidth(true); nodep->doingWidth(false); @@ -1292,6 +1347,11 @@ private: } nodep->didWidth(true); } + virtual void visit(AstInitial* nodep, AstNUser*) { + m_initialp = nodep; + nodep->iterateChildren(*this); + m_initialp = NULL; + } virtual void visit(AstNetlist* nodep, AstNUser*) { // Iterate modules backwards, in bottom-up order. That's faster nodep->iterateChildrenBackwards(*this); @@ -1473,14 +1533,15 @@ private: nodep = newp; // Process new node instead } } else { + if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp()); nodep->dtypeChgSigned(nodep->lhsp()->isSigned()); // Note there aren't yet uniops that need version changes // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned()) } int width = max(vup->c()->width(), nodep->lhsp()->width()); int ewidth = max(vup->c()->widthMin(), nodep->lhsp()->widthMin()); - nodep->width(width,ewidth); - nodep->numericFrom(nodep->lhsp()); + nodep->dtypeFrom(nodep->lhsp()); + nodep->dtypeChgWidth(width,ewidth); if (vup->c()->final()) { nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth); @@ -1519,12 +1580,13 @@ private: if (vup->c()->prelim()) { nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp()); checkCvtUS(nodep->lhsp()); checkCvtUS(nodep->rhsp()); } int width = max(vup->c()->width(), nodep->lhsp()->width()); int ewidth = max(vup->c()->widthMin(), nodep->lhsp()->widthMin()); - nodep->width(width,ewidth); + nodep->dtypeChgWidth(width,ewidth); } AstNodeBiop* shift_final(AstNodeBiop* nodep, AstNUser* vup) { // Nodep maybe edited @@ -1562,8 +1624,8 @@ private: } int width = max(vup->c()->width(), max(nodep->lhsp()->width(), nodep->rhsp()->width())); int mwidth = max(vup->c()->widthMin(), max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin())); - nodep->width(width,mwidth); - nodep->dtypeChgSigned(nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + nodep->dtypeChgWidthSigned(width,mwidth, + (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned())); if (vup->c()->final()) { // Final call, so make sure children check their sizes nodep->lhsp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); @@ -1609,7 +1671,9 @@ private: if (AstNodeBiop* newp=replaceWithDVersion(nodep)) { nodep=NULL; nodep = newp; // Process new node instead } + nodep->dtypeSetDouble(); } else { + if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp()); nodep->dtypeChgSigned(nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, nodep->isSigned())) { nodep=NULL; nodep = newp; // Process new node instead @@ -1617,7 +1681,7 @@ private: } int width = max(vup->c()->width(), max(nodep->lhsp()->width(), nodep->rhsp()->width())); int mwidth = max(vup->c()->widthMin(), max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin())); - nodep->width(width,mwidth); + nodep->dtypeChgWidth(width,mwidth); if (vup->c()->final()) { // Final call, so make sure children check their sizes nodep->lhsp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); @@ -1635,6 +1699,7 @@ private: widthCheck(nodep,"LHS",nodep->lhsp(),width,mwidth,lhsOk); widthCheck(nodep,"RHS",nodep->rhsp(),width,mwidth,rhsOk); } + //if (debug()>=9) nodep->dumpTree(cout,"-rusou-"); } void visit_math_Or_LRr(AstNodeBiop* nodep, AstNUser* vup) { // CALLER: AddD, MulD, ... @@ -1659,11 +1724,12 @@ private: // LOWER LEVEL WIDTH METHODS (none iterate) bool widthBad (AstNode* nodep, int expWidth, int expWidthMin) { + if (!nodep->dtypep()) nodep->v3fatalSrc("Under node "<prettyTypeName()<<" has no dtype?? Missing Visitor func?"); if (nodep->width()==0) nodep->v3fatalSrc("Under node "<prettyTypeName()<<" has no expected width?? Missing Visitor func?"); if (expWidth==0) nodep->v3fatalSrc("Node "<prettyTypeName()<<" has no expected width?? Missing Visitor func?"); if (expWidthMin==0) expWidthMin = expWidth; - if (nodep->widthSized() && nodep->width() != expWidthMin) return true; - if (!nodep->widthSized() && nodep->widthMin() > expWidthMin) return true; + if (nodep->dtypep()->widthSized() && nodep->width() != expWidthMin) return true; + if (!nodep->dtypep()->widthSized() && nodep->widthMin() > expWidthMin) return true; return false; } @@ -1701,7 +1767,7 @@ private: linker.relink(newp); nodep=newp; } - nodep->width(expWidth,expWidth); + nodep->dtypeChgWidth(expWidth,expWidth); UINFO(4," _new: "<width(expWidth,expWidth); + nodep->dtypeChgWidth(expWidth,expWidth); UINFO(4," _new: "<signedFlavor()) { return NULL; } + if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp()); // To simplify callers, some node types don't need to change switch (nodep->type()) { case AstType::atEQ: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; @@ -1978,6 +2045,34 @@ private: return newp; } + //---------------------------------------------------------------------- + // METHODS - data types + + AstNodeDType* moveChildDTypeEdit(AstNode* nodep) { + // DTypes at parse time get added as a childDType to some node types such as AstVars. + // We move them to global scope, so removing/changing a variable won't lose the dtype. + AstNodeDType* dtnodep = nodep->getChildDTypep(); + if (!dtnodep) nodep->v3fatalSrc("Caller should check for NULL before calling moveChild"); + UINFO(9,"moveChildDTypeEdit "<unlinkFrBack(); // Make non-child + v3Global.rootp()->typeTablep()->addTypesp(dtnodep); + return dtnodep; + } + AstNodeDType* iterateEditDTypep(AstNode* parentp, AstNodeDType* nodep) { + // Iterate into a data type to resolve that type. This process + // may eventually create a new data type, but not today + // it may make a new datatype, need subChildDType() to point to it; + // maybe we have user5p indicate a "replace me with" pointer. + // Need to be careful with "implicit" types though. + // + // Alternative is to have WidthVP return information. + // or have a call outside of normal visitor land. + // or have a m_return type (but need to return if width called multiple times) + if (!nodep) parentp->v3fatalSrc("Null dtype when widthing dtype"); + nodep->iterate(*this); + return nodep; + } + //---------------------------------------------------------------------- // METHODS - special type detection bool backRequiresUnsigned(AstNode* nodep) { @@ -1995,6 +2090,7 @@ public: m_cellRangep = NULL; m_casep = NULL; m_funcp = NULL; + m_initialp = NULL; m_doGenerate = doGenerate; } AstNode* mainAcceptEdit(AstNode* nodep) { diff --git a/src/V3WidthCommit.h b/src/V3WidthCommit.h index bad03125e..aba15e7de 100644 --- a/src/V3WidthCommit.h +++ b/src/V3WidthCommit.h @@ -63,37 +63,84 @@ public: }; //###################################################################### +// Now that all widthing is complete, +// Copy all width() to widthMin(). V3Const expects this class WidthCommitVisitor : public AstNVisitor { - // Now that all widthing is complete, - // Copy all width() to widthMin(). V3Const expects this + // NODE STATE + // AstVar::user1p -> bool, processed + AstUser1InUse m_inuser1; + 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 NULL; + // Recurse to handle the data type, as may change the size etc of this type + if (!nodep->user1()) nodep->accept(*this,NULL); + // Look for duplicate + if (AstBasicDType* bdtypep = nodep->castBasicDType()) { + AstBasicDType* newp = nodep->findInsertSameDType(bdtypep); + if (newp != bdtypep && debug()>=9) { + UINFO(9,"dtype replacement "); nodep->dumpSmall(cout); + cout<<" ----> "; newp->dumpSmall(cout); cout<width(nodep->width(),nodep->width()); - if ((nodep->width() != nodep->num().width()) || !nodep->num().sized()) { - V3Number num (nodep->fileline(), nodep->width()); + if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype"); + nodep->dtypep()->accept(*this); // Do datatype first + if ((nodep->dtypep()->width() != nodep->num().width()) + || !nodep->num().sized()) { // Need to force the number rrom unsized to sized + V3Number num (nodep->fileline(), nodep->dtypep()->width()); num.opAssign(nodep->num()); num.isSigned(nodep->isSigned()); - AstNode* newp = new AstConst(nodep->fileline(), num); + AstConst* newp = new AstConst(nodep->fileline(), num); + newp->dtypeFrom(nodep); nodep->replaceWith(newp); - //if (debug()>4) nodep->dumpTree(cout," fixConstSize_old: "); - //if (debug()>4) newp->dumpTree(cout," _new: "); - pushDeletep(nodep); nodep=NULL; + AstNode* oldp = nodep; nodep = newp; + //if (debug()>4) oldp->dumpTree(cout," fixConstSize_old: "); + //if (debug()>4) newp->dumpTree(cout," _new: "); + pushDeletep(oldp); oldp=NULL; } + editDType(nodep); } - virtual void visit(AstNode* nodep, AstNUser*) { - nodep->width(nodep->width(),nodep->width()); + virtual void visit(AstNodeDType* nodep, AstNUser*) { + // Rather than use dtypeChg which may make new nodes, we simply 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(AstNumeric::UNSIGNED); nodep->iterateChildren(*this); + nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep())); } virtual void visit(AstNodePreSel* nodep, AstNUser*) { // This check could go anywhere after V3Param nodep->v3fatalSrc("Presels should have been removed before this point"); } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + editDType(nodep); + } public: // CONSTUCTORS WidthCommitVisitor(AstNetlist* nodep) { + // Were changing widthMin's, so the table is now somewhat trashed + nodep->typeTablep()->clearCache(); nodep->accept(*this); + // Don't want to repairCache, as all needed nodes have been added back in + // a repair would prevent dead nodes from being detected } virtual ~WidthCommitVisitor() {} }; diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 32dd78650..2a4ed7aad 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -78,8 +78,12 @@ private: AstNodeDType* dtypeForExtractp(AstNode* nodep, AstNode* basefromp, int dimension, bool rangedSelect) { // Perform error checks on the node AstVar* varp = varFromBasefrom(basefromp); - //UINFO(9,"SCD\n"); if (debug()>=9) nodep->backp()->dumpTree(cout,"-selcheck: "); - AstNodeDType* ddtypep = varp->dtypep()->dtypeDimensionp(dimension); + //UINFO(9,"SCD\n"); if (debug()>=9) varp->dumpTree(cout,"-dtfexvar: "); + // This may be called on dotted variables before we've completed widthing of the dotted var. + AstNodeDType* ddtypep = varp->dtypep() ? varp->dtypep() : varp->childDTypep(); + if (!ddtypep) nodep->v3fatalSrc("No datatype found for variable in select"); + ddtypep = ddtypep->dtypeDimensionp(dimension); + if (debug()>=9 &&ddtypep) ddtypep->dumpTree(cout,"-ddtypep: "); if (AstArrayDType* adtypep = ddtypep->castArrayDType()) { return adtypep; } @@ -112,13 +116,13 @@ private: AstNode* newp = new AstSub(lhsp->fileline(), lhsp, new AstConst(lhsp->fileline(), AstConst::Unsized32(), rhs)); // We must make sure sub gets sign of original value, not from the constant - newp->numericFrom(lhsp); + newp->dtypeFrom(lhsp); return newp; } else { // rhs < 0; AstNode* newp = new AstAdd(lhsp->fileline(), lhsp, new AstConst(lhsp->fileline(), AstConst::Unsized32(), -rhs)); // We must make sure sub gets sign of original value, not from the constant - newp->numericFrom(lhsp); + newp->dtypeFrom(lhsp); return newp; } } @@ -204,6 +208,7 @@ private: AstNodeDType* ddtypep = dtypeForExtractp(nodep, basefromp, dimension, false); AstNode* fromp = nodep->lhsp()->unlinkFrBack(); AstNode* bitp = nodep->rhsp()->unlinkFrBack(); + if (debug()>=9) ddtypep->dumpTree(cout,"-ddtypep: "); if (debug()>=9) nodep->dumpTree(cout,"-vsbmd: "); if (AstArrayDType* adtypep = ddtypep->castArrayDType()) { // SELBIT(array, index) -> ARRAYSEL(array, index) diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 9fedb0a8c..aaba3bcec 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -98,6 +98,7 @@ V3Global v3Global; AstNetlist* V3Global::makeNetlist() { AstNetlist* newp = new AstNetlist(); + newp->addTypeTablep(new AstTypeTable(newp->fileline())); return newp; } diff --git a/src/verilog.y b/src/verilog.y index 0b97f6b58..3395594d2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -137,8 +137,14 @@ public: finalp->unlinkFrBack(); rangearraysp = rangesp; } + if (dtypep->implicit()) { + // It's no longer implicit but a real logic type + AstBasicDType* newp = new AstBasicDType(dtypep->fileline(), AstBasicDTypeKwd::LOGIC, + dtypep->numeric(), dtypep->width(), dtypep->widthMin()); + dtypep->deleteTree(); dtypep=NULL; + dtypep = newp; + } dtypep->rangep(finalp); - dtypep->implicit(false); } return createArray(dtypep, rangearraysp, isPacked); } @@ -3269,9 +3275,9 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, string name, AstRange if (GRAMMARP->m_varDecl == AstVarType::SUPPLY1) { nodep->addNext(V3ParseGrammar::createSupplyExpr(fileline, nodep->name(), 1)); } - // Clear any widths that got presumed by the ranging; + // Don't set dtypep in the ranging; // We need to autosize parameters and integers separately - nodep->width(0,0); + // // Propagate from current module tracing state if (nodep->isGenVar() || nodep->isParam()) nodep->trace(false); else nodep->trace(v3Global.opt.trace() && nodep->fileline()->tracingOn()); diff --git a/test_regress/t/t_cover_toggle.pl b/test_regress/t/t_cover_toggle.pl index 23acfc141..fafa7c21c 100755 --- a/test_regress/t/t_cover_toggle.pl +++ b/test_regress/t/t_cover_toggle.pl @@ -18,7 +18,7 @@ execute ( # Read the input .v file and do any CHECK_COVER requests inline_checks(); -file_grep ($Self->{stats}, qr/Coverage, Toggle points joined\s+25/i) +file_grep ($Self->{stats}, qr/Coverage, Toggle points joined\s+24/i) if $Self->{vlt}; ok(1);