diff --git a/src/V3Ast.h b/src/V3Ast.h index a7b9718ca..547461ef4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -500,10 +500,11 @@ class AstParseRefExp { public: enum en { PX_NONE, // Used in V3LinkParse only - PX_VAR_MEM, - PX_VAR_ANY, - PX_TASK, - PX_FUNC + PX_TEXT, // Unknown ID component + PX_PREDOT, // Module name or misc component above var/task/func/member + PX_VAR_MEM, // Variable that must be a memory + PX_VAR_ANY, // Variable/structure member + PX_FTASK // Task/Function (AstParse::ftaskrefp() will be set) }; enum en m_e; inline AstParseRefExp() : m_e(PX_NONE) {} @@ -512,7 +513,7 @@ public: operator en () const { return m_e; } const char* ascii() const { static const char* names[] = { - "","VAR_MEM","VAR_ANY","TASK","FUNC"}; + "","TEXT","PREDOT","VAR_MEM","VAR_ANY","FTASK"}; return names[m_e]; } }; inline bool operator== (AstParseRefExp lhs, AstParseRefExp rhs) { return (lhs.m_e == rhs.m_e); } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index cb04d822c..4cef449b3 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -806,6 +806,11 @@ void AstSenItem::dump(ostream& str) { void AstParseRef::dump(ostream& str) { this->AstNode::dump(str); str<<" ["<AstNode::dump(str); + if (start()) str<<" [START]"; } void AstActive::dump(ostream& str) { this->AstNode::dump(str); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 673a17df0..07777aca1 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1263,33 +1263,48 @@ struct AstParseRef : public AstNode { // We don't know which at parse time due to bison constraints // The link stages will replace this with AstVarRef, or AstTaskRef, etc. // Parents: math|stmt - // Children: TEXT|DOT|SEL* (or expression under sel) + // Children: TEXT|DOT|SEL*|TASK|FUNC (or expression under sel) private: AstParseRefExp m_expect; // Type we think it should resolve to + string m_name; + bool m_start; // Start of parseref stack public: - AstParseRef(FileLine* fl, AstParseRefExp expect, AstNode* lhsp) - :AstNode(fl), m_expect(expect) { setOp1p(lhsp); } + AstParseRef(FileLine* fl, AstParseRefExp expect, const string& name, AstNode* lhsp, AstNodeFTaskRef* ftaskrefp) + :AstNode(fl), m_expect(expect), m_name(name) { setNOp1p(lhsp); setNOp2p(ftaskrefp); m_start=false; } ASTNODE_NODE_FUNCS(ParseRef, PARSEREF) virtual void dump(ostream& str); - virtual V3Hash sameHash() const { return V3Hash(m_expect); } - virtual bool same(AstNode* samep) const { return expect() == samep->castParseRef()->expect(); } + virtual string name() const { return m_name; } // * = Var name + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_expect),V3Hash(m_name)); } + virtual bool same(AstNode* samep) const { return expect() == samep->castParseRef()->expect() && m_name==samep->castParseRef()->m_name; } virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } + virtual void name(const string& name) { m_name = name; } AstParseRefExp expect() const { return m_expect; } + void expect(AstParseRefExp exp) { m_expect=exp; } // op1 = Components AstNode* lhsp() const { return op1p(); } // op1 = List of statements + AstNode* ftaskrefp() const { return op2p(); } // op2 = Function/task reference + void ftaskrefp(AstNodeFTaskRef* nodep) { setNOp2p(nodep); } // op2 = Function/task reference + bool start() const { return m_start; } + void start(bool flag) { m_start = flag; } }; struct AstDot : public AstNode { // A dot separating paths in an AstXRef, AstFuncRef or AstTaskRef - // These are elimiated in the link stage + // These are eliminated in the link stage +private: + bool m_start; // Start of parseref stack +public: AstDot(FileLine* fl, AstNode* lhsp, AstNode* rhsp) - :AstNode(fl) { setOp1p(lhsp); setOp2p(rhsp); } + :AstNode(fl) { setOp1p(lhsp); setOp2p(rhsp); m_start=false; } ASTNODE_NODE_FUNCS(Dot, DOT) + virtual void dump(ostream& str); virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } AstNode* lhsp() const { return op1p(); } AstNode* rhsp() const { return op2p(); } + bool start() const { return m_start; } + void start(bool flag) { m_start = flag; } }; //###################################################################### diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 0e79994a1..1ee33ed15 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -773,9 +773,9 @@ class LinkDotParamVisitor : public AstNVisitor { private: // NODE STATE // Cleared on global - // *:user1p() -> See LinkDotState - // *:user2p() -> See LinkDotState - // *:user4() -> See LinkDotState + // *::user1p() -> See LinkDotState + // *::user2p() -> See LinkDotState + // *::user4() -> See LinkDotState // STATE LinkDotState* m_statep; // State to pass between visitors, including symbol table @@ -786,7 +786,10 @@ private: void pinImplicitExprRecurse(AstNode* nodep) { // Under a pin, Check interconnect expression for a pin reference or a concat. // Create implicit variable as needed - if (nodep->castVarRef()) { + if (nodep->castDot()) { // Not creating a simple implied type, + // and implying something else would just confuse later errors + } + if (nodep->castVarRef() || (nodep->castParseRef() && nodep->castParseRef()->start())) { // To prevent user errors, we should only do single bit // implicit vars, however some netlists (MIPS) expect single // bit implicit wires to get created with range 0:0 etc. @@ -982,47 +985,37 @@ class LinkDotResolveVisitor : public AstNVisitor { private: // NODE STATE // Cleared on global - // *:user1p() -> See LinkDotState - // *:user2p() -> See LinkDotState - // *:user4() -> See LinkDotState + // *::user1p() -> See LinkDotState + // *::user2p() -> See LinkDotState + // *::user3() // bool. Processed + // *::user4() -> See LinkDotState // Cleared on Cell - // AstVar::user5() // bool True if pin used in this cell + // AstVar::user5() // bool. True if pin used in this cell + AstUser3InUse m_inuser3; AstUser5InUse m_inuser5; + // TYPES + enum DotPosition { DP_SCOPE, DP_VARETC, DP_MEMBER }; + // STATE LinkDotState* m_statep; // State, including dotted symbol table VSymEnt* m_curSymp; // SymEnt for current lookup point VSymEnt* m_modSymp; // SymEnt for current module + VSymEnt* m_dotSymp; // SymEnt for dotted AstParse lookup VSymEnt* m_pinSymp; // SymEnt for pin lookups AstCell* m_cellp; // Current cell AstNodeModule* m_modp; // Current module AstNodeFTask* m_ftaskp; // Current function/task + AstDot* m_dotp; // Current dot + DotPosition m_dotPos; // Scope part of dotted resolution + bool m_dotErr; // Error found in dotted resolution, ignore upwards + string m_dotText; // String of dotted names found in below parseref int debug() { return LinkDotState::debug(); } // METHODS - Variables - bool linkVarName (VSymEnt* lookupSymp, AstVarRef* nodep) { - // Return true if changed, and caller should end processing - if (!nodep->varp()) { - UINFO(9," linkVarName se"<<(void*)lookupSymp<<" n="<v3fatalSrc("NULL lookup symbol table"); - VSymEnt* foundp = lookupSymp->findIdFallback(nodep->name()); - if (AstVar* varp = foundp->nodep()->castVar()) { - nodep->varp(varp); - nodep->packagep(foundp->packagep()); - } - else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) { - AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep()); - nodep->replaceWith(newp); - nodep->deleteTree(); nodep=NULL; - return true; // Edited - } - } - return false; - } void createImplicitVar (VSymEnt* lookupSymp, AstVarRef* nodep, AstNodeModule* modp, VSymEnt* moduleSymp, bool noWarn) { // Create implicit after warning - if (linkVarName(lookupSymp, nodep)) { nodep=NULL; return; } if (!nodep->varp()) { if (!noWarn) { if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) { @@ -1054,18 +1047,18 @@ private: virtual void visit(AstNodeModule* nodep, AstNUser*) { if (nodep->dead()) return; UINFO(8," "<getNodeSym(nodep); // Until overridden by a SCOPE + m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep); // Until overridden by a SCOPE m_cellp = NULL; m_modp = nodep; nodep->iterateChildren(*this); m_modp = NULL; - m_curSymp = m_modSymp = NULL; + m_dotSymp = m_curSymp = m_modSymp = NULL; } virtual void visit(AstScope* nodep, AstNUser*) { UINFO(8," "<getScopeSym(nodep); + m_dotSymp = m_curSymp = m_statep->getScopeSym(nodep); nodep->iterateChildren(*this); - m_curSymp = NULL; + m_dotSymp = m_curSymp = NULL; } virtual void visit(AstCellInline* nodep, AstNUser*) { if (m_statep->forScopeCreation()) { @@ -1129,20 +1122,214 @@ private: } // Early return() above when deleted } - virtual void visit(AstVarRef* nodep, AstNUser*) { - // VarRef: Resolve its reference - nodep->iterateChildren(*this); - if (!nodep->varp()) { - if (linkVarName(m_curSymp, nodep)) { nodep=NULL; return; } - if (!nodep->varp()) { - bool err = (m_statep->forPrimary() - && !m_statep->implicitOk(m_modp, nodep->name())); + virtual void visit(AstDot* nodep, AstNUser*) { + if (nodep->user3SetOnce()) return; + UINFO(8," "<start(); // Save, as nodep may go NULL + { + m_dotp = nodep; // Always, not just at start + if (start) { // Starting dot sequence + if (debug()>=9) nodep->dumpTree("-dot-in: "); + m_dotText = ""; + m_dotErr = false; + m_dotSymp = m_curSymp; // Start from current point + } + // m_dotText communicates the cell prefix between stages + m_dotPos = DP_SCOPE; + nodep->lhsp()->iterateAndNext(*this); + if (!m_dotErr) { // Once something wrong, give up + if (start && m_dotPos==DP_SCOPE) m_dotPos = DP_VARETC; // Top dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS + nodep->rhsp()->iterateAndNext(*this); + } + if (start) { + AstNode* newp; + if (m_dotErr) { + newp = new AstConst(nodep->fileline(),AstConst::LogicFalse()); + } else { + // RHS is what we're left with + newp = nodep->rhsp()->unlinkFrBack(); + } + if (debug()>=9) newp->dumpTree("-dot-out: "); + nodep->replaceWith(newp); + pushDeletep(nodep); nodep=NULL; + } else { // Dot midpoint + AstNode* newp = nodep->rhsp()->unlinkFrBack(); + nodep->replaceWith(newp); + pushDeletep(nodep); nodep=NULL; + } + } + m_dotp = lastDotp; + if (start) { + m_dotText = lastText; + m_dotErr = lastErr; + m_dotPos = lastDotPos; + m_dotSymp = lastDotSymp; + } + } + virtual void visit(AstParseRef* nodep, AstNUser*) { + if (nodep->user3SetOnce()) return; + UINFO(9," linkPARSEREF se"<<(void*)m_dotSymp<<" pos="<v3fatalSrc("NULL lookup symbol table"); + if (!m_statep->forPrimary()) nodep->v3fatalSrc("ParseRefs should no longer exist"); + AstDot* lastDotp = m_dotp; + string lastText = m_dotText; + bool lastErr = m_dotErr; + DotPosition lastDotPos = m_dotPos; + VSymEnt* lastDotSymp = m_dotSymp; + bool start = nodep->start(); // Save, as nodep may go NULL + if (nodep->start()) { + m_dotp = NULL; + m_dotText = ""; + m_dotErr = false; + m_dotPos = DP_VARETC; + m_dotSymp = m_curSymp; + } + if (m_dotPos == DP_MEMBER) { + nodep->v3error("Unsupported: Structs and dotted reference into variable"); + } + else { + // + string expectWhat; + bool allowScope = false; + bool allowVar = false; + bool onlyVar = false; + if (nodep->expect() == AstParseRefExp::PX_PREDOT) { + // {a}.{b}, where {a} maybe a module name + // FUTURE: or variable, where dotting into structure member + expectWhat = "scope/variable"; + allowScope = true; + allowVar = true; + } else if (nodep->expect() == AstParseRefExp::PX_VAR_MEM + || nodep->expect() == AstParseRefExp::PX_VAR_ANY) { + expectWhat = "variable"; + onlyVar = true; + allowVar = true; + } else if (nodep->expect() == AstParseRefExp::PX_FTASK) { + expectWhat = "task/function"; + } else { + nodep->v3fatalSrc("Unhandled AstParseRefExp"); + } + // Lookup + VSymEnt* foundp; + string baddot; + VSymEnt* okSymp = NULL; + if (allowScope) { + foundp = m_statep->findDotted(m_dotSymp, nodep->name(), baddot, okSymp); // Maybe NULL + } else { + foundp = m_dotSymp->findIdFallback(nodep->name()); + } + if (foundp) UINFO(9," found=se"<<(void*)foundp<<" n="<nodep()<nodep()->castCell() || foundp->nodep()->castBegin() + || foundp->nodep()->castModule()) { // if top + if (allowScope) { + ok = true; + if (m_dotText!="") m_dotText += "."; + m_dotText += nodep->name(); + m_dotSymp = foundp; + // Upper AstDot visitor will handle it from here + } + } + else if (AstVar* varp = foundp->nodep()->castVar()) { + if (allowVar) { + AstNodeVarRef* newp; + if (m_dotText != "") { + newp = new AstVarXRef(nodep->fileline(), nodep->name(), m_dotText, false); // lvalue'ness computed later + newp->varp(varp); + m_dotText = ""; + } else { + newp = new AstVarRef(nodep->fileline(), nodep->name(), false); // lvalue'ness computed later + newp->varp(varp); + newp->packagep(foundp->packagep()); + } + nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL; + m_dotPos = DP_MEMBER; + ok = true; + } + } + else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) { + if (allowVar) { + AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep()); + nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL; + ok = true; + m_dotText = ""; + } + } + else if (AstNodeFTask* ftaskp = foundp->nodep()->castNodeFTask()) { + if (nodep->expect() == AstParseRefExp::PX_FTASK) { + AstNodeFTaskRef* refp = nodep->ftaskrefp()->castNodeFTaskRef(); + if (!refp) nodep->v3fatalSrc("Parseref indicates FTASKref but none found"); + refp->name(nodep->name()); + refp->dotted(m_dotText); // Maybe "" + refp->taskp(ftaskp); + refp->packagep(foundp->packagep()); // Generally set by parse, but might be an import + taskFuncSwapCheck(refp); + refp->unlinkFrBack(); + nodep->replaceWith(refp); pushDeletep(nodep); nodep = NULL; + ok = true; + } + } + // + if (!ok) { + bool checkImplicit = (onlyVar && m_dotText==""); + bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name())); if (err) { m_statep->preErrorDump(); - nodep->v3error("Can't find definition of signal: "<prettyName()); + if (foundp) { + nodep->v3error("Found definition of '"<prettyName() + <<"'"<<" as a "<nodep()->typeName() + <<" but expected a "<v3error("Can't find definition of "<prettyName()); + } else { + nodep->v3error("Can't find definition of '"<<(baddot!=""?baddot:nodep->prettyName())<<"' in dotted " + <prettyName()); + okSymp->cellErrorScopes(nodep, AstNode::prettyName(m_dotText)); + } + m_dotErr = true; } - // Create even if error, so only complain once - createImplicitVar (m_curSymp, nodep, m_modp, m_modSymp, err); + if (checkImplicit) { // Else if a scope is allowed, making a signal won't help error cascade + // Create if implicit, and also if error (so only complain once) + AstVarRef* newp = new AstVarRef(nodep->fileline(), nodep->name(), false); + nodep->replaceWith(newp); + pushDeletep(nodep); nodep = NULL; + createImplicitVar (m_curSymp, newp, m_modp, m_modSymp, err); + } + } + } + if (start) { + m_dotp = lastDotp; + m_dotText = lastText; + m_dotErr = lastErr; + m_dotPos = lastDotPos; + m_dotSymp = lastDotSymp; + } + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + // VarRef: Resolve its reference + // ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find + // errors here now that we have a VarRef. + nodep->iterateChildren(*this); + if (!nodep->varp()) { + UINFO(9," linkVarRef se"<<(void*)m_curSymp<<" n="<v3fatalSrc("NULL lookup symbol table"); + VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); + if (AstVar* varp = foundp->nodep()->castVar()) { + nodep->varp(varp); + nodep->packagep(foundp->packagep()); // Generally set by parse, but might be an import + } + if (!nodep->varp()) { + m_statep->preErrorDump(); + nodep->v3error("Can't find definition of signal, again: "<prettyName()); } } } @@ -1150,6 +1337,7 @@ private: // VarRef: Resolve its reference // We always link even if varp() is set, because the module we choose may change // due to creating new modules, flattening, etc. + if (nodep->user3SetOnce()) return; UINFO(8," "<user3SetOnce()) return; UINFO(8," "<packagep() && nodep->taskp()) { // References into packages don't care about cell hierarchy. @@ -1259,15 +1448,30 @@ private: } nodep->iterateChildren(*this); } + virtual void visit(AstSelBit* nodep, AstNUser*) { + if (nodep->user3SetOnce()) return; + if (m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} + nodep->lhsp()->iterateAndNext(*this); + if (AstConst* constp = nodep->rhsp()->castConst()) { + string index = AstNode::encodeNumber(constp->toSInt()); + m_dotText += "__BRA__"+index+"__KET__"; + } else { + nodep->v3error("Unsupported: Non-constant inside []'s in the cell part of a dotted reference"); + } + // And pass up m_dotText + } else { + nodep->iterateChildren(*this); + } + } virtual void visit(AstBegin* nodep, AstNUser*) { UINFO(5," "<getNodeSym(nodep); + m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); UINFO(5," cur=se"<<(void*)m_curSymp<iterateChildren(*this); } - m_curSymp = oldCurSymp; + m_dotSymp = m_curSymp = oldCurSymp; UINFO(5," cur=se"<<(void*)m_curSymp<getNodeSym(nodep); + m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); nodep->iterateChildren(*this); } - m_curSymp = oldCurSymp; + m_dotSymp = m_curSymp = oldCurSymp; m_ftaskp = NULL; } virtual void visit(AstRefDType* nodep, AstNUser*) { // Resolve its reference + if (nodep->user3SetOnce()) return; if (!nodep->defp()) { VSymEnt* foundp; if (nodep->packagep()) { @@ -1324,10 +1529,14 @@ public: m_statep = statep; m_modSymp = NULL; m_curSymp = NULL; + m_dotSymp = NULL; m_pinSymp = NULL; m_cellp = NULL; m_modp = NULL; m_ftaskp = NULL; + m_dotp = NULL; + m_dotPos = DP_VARETC; + m_dotErr = false; // rootp->accept(*this); } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 09d49650c..c5e035580 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -19,11 +19,11 @@ //************************************************************************* // LinkParse TRANSFORMATIONS: // Top-down traversal -// Replace ParseRef with VarRef, VarXRef, FuncRef or TaskRef -// TASKREF(PARSEREF(DOT(TEXTa,TEXTb))) -> TASKREF(a,b) -// PARSEREF(TEXTa) -> VARREF(a) -// PARSEREF(DOT(TEXTa,TEXTb)) -> VARXREF("a","b") -// PARSEREF(DOT(DOT(TEXTa,TEXTb),TEXTc)) -> VARXREF("a.b","c") +// Replace ParseRef with lower parseref +// TASKREF(PARSEREF(DOT(TEXTa,TEXTb))) -> DOT(PARSEREF(a),TASKREF(b)) +// PARSEREF(TEXTa) -> PARSEREF(a) (no change) +// PARSEREF(DOT(TEXTa,TEXTb)) -> DOT(PARSEREF(A),PARSEREF(b) +// PARSEREF(DOT(DOT(TEXTa,TEXTb),TEXTc)) -> DOT(DOT(PARSEREF(a),PARSEREF(b)),PARSEREF(c)) //************************************************************************* #include "config_build.h" @@ -57,17 +57,16 @@ private: typedef set FileLineSet; // STATE - string m_dotText; // Dotted module text we are building for a dotted node, passed up - bool m_inModDot; // We're inside module part of dotted name - AstParseRefExp m_exp; // Type of data we're looking for - AstText* m_baseTextp; // Lowest TEXT node that needs replacement with varref - AstVar* m_varp; // Variable we're under + AstParseRefExp m_exp; // Type of data we're looking for + AstVar* m_varp; // Variable we're under ImplTypedefMap m_implTypedef; // Created typedefs for each FileLineSet m_filelines; // Filelines that have been seen bool m_inAlways; // Inside an always bool m_inGenerate; // Inside a generate + bool m_needStart; // Need start marker on lower AstParse AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module AstNodeModule* m_modp; // Current module + AstParseRef* m_rightParsep; // RHS()-most parseref // METHODS static int debug() { @@ -104,17 +103,6 @@ private: cleanFileline(nodep); UINFO(5," "<namep()) { - m_exp = AstParseRefExp::PX_FUNC; - nodep->namep()->accept(*this); // No next, we don't want to do the edit - m_exp = AstParseRefExp::PX_NONE; - if (!m_baseTextp) nodep->v3fatalSrc("No TEXT found to indicate function name"); - nodep->name(m_baseTextp->text()); - nodep->dotted(m_dotText); - nodep->namep()->unlinkFrBack()->deleteTree(); m_baseTextp=NULL; - } AstNodeModule* upperValueModp = m_valueModp; m_valueModp = NULL; nodep->iterateChildren(*this); @@ -122,99 +110,81 @@ private: } } virtual void visit(AstParseRef* nodep, AstNUser*) { + if (nodep->user1SetOnce()) return; // Process only once. // VarRef: Parse its reference UINFO(5," "<lhsp()->unlinkFrBack(); - nodep->replaceWith(lhsp); - - // Process lower nodes - m_dotText = ""; - m_baseTextp = NULL; - if (m_exp == AstParseRefExp::PX_FUNC) { - lhsp->accept(*this); - // Return m_dotText to invoker - } else if (nodep->expect() == AstParseRefExp::PX_VAR_MEM - || nodep->expect() == AstParseRefExp::PX_VAR_ANY) { - m_exp = nodep->expect(); - lhsp->accept(*this); + { // First do any function call + AstParseRefExp lastExp = m_exp; + { m_exp = AstParseRefExp::PX_NONE; - if (!m_baseTextp) nodep->v3fatalSrc("No TEXT found to indicate function name"); - if (m_dotText == "") { - AstNode* newp = new AstVarRef(nodep->fileline(), m_baseTextp->text(), false); // lvalue'ness computed later - m_baseTextp->replaceWith(newp); m_baseTextp->deleteTree(); m_baseTextp=NULL; - } else { - AstNode* newp = new AstVarXRef(nodep->fileline(), m_baseTextp->text(), m_dotText, false); // lvalue'ness computed later - m_baseTextp->replaceWith(newp); m_baseTextp->deleteTree(); m_baseTextp=NULL; - } - } else { - nodep->v3fatalSrc("Unknown ParseRefExp type\n"); + if (nodep->ftaskrefp()) nodep->ftaskrefp()->iterateAndNext(*this); } - nodep->deleteTree(); nodep=NULL; + m_exp = lastExp; } - if (m_exp != AstParseRefExp::PX_FUNC) { // Fuctions need to look at the name themself - m_dotText = oldText; - m_inModDot = oldDot; - m_exp = oldExp; - m_baseTextp = oldBasep; + if (nodep->start()) { // Start of new parseref stack + // The start parseref indicates the type of element to be created. + // Push the start marking down to the lowest non-start parseref under any DOTs. + // May be a varref inside a select, etc, so save state and recurse + AstParseRefExp lastExp = m_exp; + AstParseRef* lastRightp = m_rightParsep; + { + m_exp = nodep->expect(); + m_rightParsep = NULL; + m_needStart = true; + nodep->lhsp()->accept(*this); + if (!m_rightParsep) nodep->v3fatalSrc("No child ParseRef found"); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + if (nodep->expect() == AstParseRefExp::PX_FTASK) { + // Put the FTaskRef under the final DOT + AstNodeFTaskRef* ftaskrefp = nodep->ftaskrefp()->castNodeFTaskRef(); + if (!ftaskrefp) nodep->v3fatalSrc("Parseref indicates FTASKref but none found"); + ftaskrefp->name(m_rightParsep->name()); + ftaskrefp->unlinkFrBack(); + if (ftaskrefp->packagep()) { + // Can remove the parse stack entirely and V3LinkDot will deal with it natively + lhsp = ftaskrefp; + } else { + m_rightParsep->ftaskrefp(ftaskrefp); + } + } + nodep->replaceWith(lhsp); + nodep->deleteTree(); nodep=NULL; + } + m_exp = lastExp; + m_rightParsep = lastRightp; + } + else { // inside existing stack + nodep->expect(m_exp); + m_rightParsep = nodep; + // Move the start marker down to a standalone parse reference + if (m_needStart) { nodep->start(true); m_needStart = false; } } } virtual void visit(AstDot* nodep, AstNUser*) { UINFO(5," "<user1SetOnce()) { // Process only once. + cleanFileline(nodep); + if (m_exp == AstParseRefExp::PX_NONE) nodep->v3fatalSrc("Tree syntax error: Not expecting "<type()<<" under a "<backp()->type()); + AstParseRefExp lastExp = m_exp; + m_exp = AstParseRefExp::PX_PREDOT; + if (m_needStart) { nodep->start(true); m_needStart = false; } nodep->lhsp()->iterateAndNext(*this); - string namelhs = m_dotText; - - m_dotText = ""; + m_exp = lastExp; nodep->rhsp()->iterateAndNext(*this); - m_dotText = namelhs + "." + m_dotText; - } else { // Not in ModDot, so this is {modulepart} DOT {name} - m_inModDot = true; - m_dotText = ""; - nodep->lhsp()->iterateAndNext(*this); - string namelhs = m_dotText; - - m_inModDot = false; - m_dotText = ""; - nodep->rhsp()->iterateAndNext(*this); - m_dotText = namelhs; - - nodep->replaceWith(nodep->rhsp()->unlinkFrBack()); nodep->deleteTree(); nodep=NULL; } } virtual void visit(AstSelBit* nodep, AstNUser*) { if (!nodep->user1SetOnce()) { // Process only once. cleanFileline(nodep); - if (m_inModDot) { // Already under dot, so this is {modulepart} DOT {modulepart} - m_dotText = ""; - nodep->lhsp()->iterateAndNext(*this); - if (AstConst* constp = nodep->rhsp()->castConst()) { - string index = AstNode::encodeNumber(constp->toSInt()); - m_dotText = m_dotText+"__BRA__"+index+"__KET__"; - } else { - nodep->v3error("Unsupported: Non-constant inside []'s in the cell part of a dotted reference"); - } - // And pass up m_dotText - } else if (m_exp==AstParseRefExp::PX_FUNC) { - nodep->v3error("Syntax Error: Range selection '[]' is not allowed as part of function names"); + if (m_exp==AstParseRefExp::PX_FTASK) { + nodep->v3error("Syntax Error: Range selection '[]' is not allowed as part of function/task names"); } else { nodep->lhsp()->iterateAndNext(*this); AstParseRefExp lastExp = m_exp; - AstText* lasttextp = m_baseTextp; { m_exp = AstParseRefExp::PX_NONE; nodep->rhsp()->iterateAndNext(*this); } - m_baseTextp = lasttextp; m_exp = lastExp; } } @@ -223,40 +193,24 @@ private: // Excludes simple AstSel, see above if (!nodep->user1SetOnce()) { // Process only once. cleanFileline(nodep); - if (m_inModDot) { // Already under dot, so this is {modulepart} DOT {modulepart} + if (m_exp == AstParseRefExp::PX_PREDOT) { // Already under dot, so this is {modulepart} DOT {modulepart} nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the cell part of a dotted reference"); - } else if (m_exp==AstParseRefExp::PX_FUNC) { - nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed as part of function names"); + } else if (m_exp==AstParseRefExp::PX_FTASK) { + nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed as part of function/task names"); } else if (m_exp==AstParseRefExp::PX_VAR_MEM) { nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed when expecting memory reference"); } else { nodep->lhsp()->iterateAndNext(*this); AstParseRefExp lastExp = m_exp; - AstText* lasttextp = m_baseTextp; { m_exp = AstParseRefExp::PX_NONE; nodep->rhsp()->iterateAndNext(*this); nodep->thsp()->iterateAndNext(*this); } - m_baseTextp = lasttextp; m_exp = lastExp; } } } - virtual void visit(AstText* nodep, AstNUser*) { - if (!nodep->user1SetOnce()) { // Process only once. - cleanFileline(nodep); - if (m_exp != AstParseRefExp::PX_NONE) { - UINFO(7," "<text(); - } else { - if (m_baseTextp) nodep->v3fatalSrc("Multiple low-level ParseRef text's found; which one is var name?"); - m_baseTextp = nodep; - } - } - } - } virtual void visit(AstEnumItem* nodep, AstNUser*) { // Expand ranges cleanFileline(nodep); @@ -470,14 +424,14 @@ private: public: // CONSTUCTORS LinkParseVisitor(AstNetlist* rootp) { - m_inModDot = false; - m_exp = AstParseRefExp::PX_NONE; - m_baseTextp = NULL; m_varp = NULL; + m_exp = AstParseRefExp::PX_NONE; m_modp = NULL; m_inAlways = false; m_inGenerate = false; + m_needStart = false; m_valueModp = NULL; + m_rightParsep = NULL; rootp->accept(*this); } virtual ~LinkParseVisitor() {} diff --git a/src/verilog.y b/src/verilog.y index 2e0ebb908..1450fe0ca 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2135,17 +2135,17 @@ for_step: // IEEE: for_step //************************************************ // Functions/tasks -taskRef: // IEEE: part of tf_call - idDotted { $$ = new AstTaskRef($1->fileline(),new AstParseRef($1->fileline(), AstParseRefExp::PX_TASK, $1),NULL);} - | idDotted '(' list_of_argumentsE ')' { $$ = new AstTaskRef($1->fileline(),new AstParseRef($1->fileline(), AstParseRefExp::PX_TASK, $1),$3);} - //UNSUP: package_scopeIdFollows idDotted { $$ = new AstTaskRef($1->fileline(),new AstParseRef($2->fileline(), AstParseRefExp::PX_TASK, $2),NULL);} - //UNSUP: package_scopeIdFollows idDotted '(' list_of_argumentsE ')' { $$ = new AstTaskRef($1->fileline(),new AstParseRef($2->fileline(), AstParseRefExp::PX_TASK, $2),$4);} +taskRef: // IEEE: part of tf_call + idDotted { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_FTASK, "", $1, new AstTaskRef($1->fileline(),"",NULL)); $$->start(true); } + | idDotted '(' list_of_argumentsE ')' { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_FTASK, "", $1, new AstTaskRef($1->fileline(),"",$3)); $$->start(true); } + //UNSUP: package_scopeIdFollows idDotted { } + //UNSUP: package_scopeIdFollows idDotted '(' list_of_argumentsE ')' { } //UNSUP: idDotted is really just id to allow dotted method calls ; -funcRef: // IEEE: part of tf_call - idDotted '(' list_of_argumentsE ')' { $$ = new AstFuncRef($2,new AstParseRef($1->fileline(), AstParseRefExp::PX_FUNC, $1), $3); } - | package_scopeIdFollows idDotted '(' list_of_argumentsE ')' { $$ = new AstFuncRef($3,new AstParseRef($2->fileline(), AstParseRefExp::PX_FUNC, $2), $4); $$->packagep($1); } +funcRef: // IEEE: part of tf_call + idDotted '(' list_of_argumentsE ')' { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_FTASK, "", $1, new AstFuncRef($2, "", $3)); $$->start(true); } + | package_scopeIdFollows idDotted '(' list_of_argumentsE ')' { AstFuncRef* f=new AstFuncRef($3,"",$4); f->packagep($1); $$ = new AstParseRef($2->fileline(), AstParseRefExp::PX_FTASK, "", $2, f); $$->start(true); } //UNSUP: idDotted is really just id to allow dotted method calls ; @@ -2978,12 +2978,12 @@ variable_lvalueConcList: // IEEE: part of variable_lvalue: '{' variable_l // VarRef to a Memory varRefMem: - idDotted { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_VAR_MEM, $1); } + idDotted { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_VAR_MEM, "", $1, NULL); $$->start(true); } ; // VarRef to dotted, and/or arrayed, and/or bit-ranged variable idClassSel: // Misc Ref to dotted, and/or arrayed, and/or bit-ranged variable - idDotted { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_VAR_ANY, $1); } + idDotted { $$ = new AstParseRef($1->fileline(), AstParseRefExp::PX_VAR_ANY, "", $1, NULL); $$->start(true); } // // IEEE: [ implicit_class_handle . | package_scope ] hierarchical_variable_identifier select //UNSUP yTHIS '.' idDotted { UNSUP } //UNSUP ySUPER '.' idDotted { UNSUP } @@ -3008,7 +3008,7 @@ idDottedMore: // id below includes: // enum_identifier idArrayed: // IEEE: id + select - id { $$ = new AstText($1,*$1); } + id { $$ = new AstParseRef($1,AstParseRefExp::PX_TEXT,*$1,NULL,NULL); } // // IEEE: id + part_select_range/constant_part_select_range | idArrayed '[' expr ']' { $$ = new AstSelBit($2,$1,$3); } // Or AstArraySel, don't know yet. | idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract($2,$1,$3,$5); } diff --git a/test_regress/t/t_var_notfound_bad.pl b/test_regress/t/t_var_notfound_bad.pl index f567ed80d..fb793da88 100755 --- a/test_regress/t/t_var_notfound_bad.pl +++ b/test_regress/t/t_var_notfound_bad.pl @@ -10,11 +10,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di compile ( fails=>1, expect=> -'%Error: t/t_var_notfound_bad.v:\d+: Can\'t find definition of signal: nf -%Error: t/t_var_notfound_bad.v:\d+: Can\'t find definition of \'subsubz\' in dotted signal: sub.subsubz.inss -%Error: Known scopes under \'inss\': subsub +'%Error: t/t_var_notfound_bad.v:\d+: Can\'t find definition of variable: nf +%Error: t/t_var_notfound_bad.v:\d+: Can\'t find definition of \'subsubz\' in dotted scope/variable: sub.subsubz +%Error: Known scopes under \'sub\': subsub %Error: t/t_var_notfound_bad.v:\d+: Can\'t find definition of task/function: nofunc %Error: t/t_var_notfound_bad.v:\d+: Can\'t find definition of task/function: notask +%Error: t/t_var_notfound_bad.v:\d+: Found definition of \'a_var\' as a VAR but expected a task/function %Error: Exiting due to.*', ); diff --git a/test_regress/t/t_var_notfound_bad.v b/test_regress/t/t_var_notfound_bad.v index a5f3c34d9..95c27b485 100644 --- a/test_regress/t/t_var_notfound_bad.v +++ b/test_regress/t/t_var_notfound_bad.v @@ -6,6 +6,7 @@ module t (/*AUTOARG*/); integer i; + integer a_var; sub sub (); @@ -14,6 +15,7 @@ module t (/*AUTOARG*/); sub.subsubz.inss = 0; // subsub not found i = nofunc(); // nofunc not found notask(); // notask not found + a_var(); // Calling variable as task $finish; end endmodule