diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 33d275e4e..95ce8ba24 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -673,6 +673,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const { const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true); const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true); info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">"; + } else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) { + const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true); + info.m_type = "VlAssocArray"; } else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) { const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true); info.m_type = "VlQueue<" + sub.m_type + ">"; @@ -1683,6 +1686,10 @@ string AstQueueDType::prettyDTypeName() const { if (boundConst()) str += ":" + cvtToStr(boundConst()); return str + "]"; } +void AstWildcardArrayDType::dumpSmall(std::ostream& str) const { + this->AstNodeDType::dumpSmall(str); + str << "[*]"; +} void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "[]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index d9e4b81db..8b258db0a 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -278,6 +278,17 @@ public: virtual bool same(const AstNode* samep) const override { return true; } }; +class AstWildcardRange final : public AstNodeRange { + // Wildcard range specification, for wildcard index type associative arrays +public: + explicit AstWildcardRange(FileLine* fl) + : ASTGEN_SUPER_WildcardRange(fl) {} + ASTNODE_NODE_FUNCS(WildcardRange) + virtual string emitC() { V3ERROR_NA_RETURN(""); } + virtual string emitVerilog() { return "[*]"; } + virtual bool same(const AstNode* samep) const override { return true; } +}; + class AstGatePin final : public AstNodeMath { // Possibly expand a gate primitive input pin value to match the range of the gate primitive public: @@ -819,6 +830,62 @@ public: virtual bool isCompound() const override { return true; } }; +class AstWildcardArrayDType final : public AstNodeDType { + // Wildcard index type associative array data type, ie "some_dtype var_name [*]" + // Children: DTYPE (moved to refDTypep() in V3Width) +private: + AstNodeDType* m_refDTypep; // Elements of this type (after widthing) +public: + AstWildcardArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp) + : ASTGEN_SUPER_WildcardArrayDType(fl) { + childDTypep(dtp); // Only for parser + refDTypep(nullptr); + dtypep(nullptr); // V3Width will resolve + } + ASTNODE_NODE_FUNCS(WildcardArrayDType) + virtual const char* broken() const override { + BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) + || (!m_refDTypep && childDTypep()))); + return nullptr; + } + virtual void cloneRelink() override { + if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep(); + } + virtual bool same(const AstNode* samep) const override { + const AstNodeArrayDType* const asamep = static_cast(samep); + if (!asamep->subDTypep()) return false; + return (subDTypep() == asamep->subDTypep()); + } + virtual bool similarDType(AstNodeDType* samep) const override { + const AstNodeArrayDType* const asamep = static_cast(samep); + return type() == samep->type() && asamep->subDTypep() + && subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()); + } + virtual void dumpSmall(std::ostream& str) const override; + virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); } + // op1 = Range of variable + AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); } + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + virtual AstNodeDType* subDTypep() const override { + return m_refDTypep ? m_refDTypep : childDTypep(); + } + void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } + virtual AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } + virtual void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } + // METHODS + virtual AstBasicDType* basicp() const override { return subDTypep()->basicp(); } + virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } + virtual int widthAlignBytes() const override { + return sizeof(std::map); + } + virtual int widthTotalBytes() const override { + return sizeof(std::map); + } + virtual bool isCompound() const override { return true; } +}; + class AstBasicDType final : public AstNodeDType { // Builtin atomic/vectored data type // Children: RANGE (converted to constant in V3Width) @@ -1686,6 +1753,44 @@ public: virtual int instrCount() const override { return widthInstrs(); } }; +class AstWildcardSel final : public AstNodeSel { + // Parents: math|stmt + // Children: varref|arraysel, math +private: + void init(AstNode* fromp) { + if (fromp && VN_IS(fromp->dtypep()->skipRefp(), WildcardArrayDType)) { + // Strip off array to find what array references + dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), WildcardArrayDType)->subDTypep()); + } + } + +public: + AstWildcardSel(FileLine* fl, AstNode* fromp, AstNode* bitp) + : ASTGEN_SUPER_WildcardSel(fl, fromp, bitp) { + init(fromp); + } + ASTNODE_NODE_FUNCS(WildcardSel) + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override { + return new AstWildcardSel{this->fileline(), lhsp, rhsp}; + } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { + V3ERROR_NA; + } + virtual string emitVerilog() override { return "%k(%l%f[%r])"; } + virtual string emitC() override { return "%li%k[%ri]"; } + virtual bool cleanOut() const override { return true; } + virtual bool cleanLhs() const override { return false; } + virtual bool cleanRhs() const override { return true; } + virtual bool sizeMattersLhs() const override { return false; } + virtual bool sizeMattersRhs() const override { return false; } + virtual bool isGateOptimizable() const override { + return true; + } // esp for V3Const::ifSameAssign + virtual bool isPredictOptimizable() const override { return false; } + virtual bool same(const AstNode* samep) const override { return true; } + virtual int instrCount() const override { return widthInstrs(); } +}; + class AstWordSel final : public AstNodeSel { // Select a single word from a multi-word wide value public: @@ -4887,6 +4992,47 @@ public: virtual bool same(const AstNode* samep) const override { return true; } }; +class AstConsWildcard final : public AstNodeMath { + // Construct a wildcard assoc array and return object, '{} + // Parents: math + // Children: expression (elements or other queues) +public: + AstConsWildcard(FileLine* fl, AstNode* defaultp) + : ASTGEN_SUPER_ConsWildcard(fl) { + setNOp1p(defaultp); + } + ASTNODE_NODE_FUNCS(ConsWildcard) + virtual string emitVerilog() override { return "'{}"; } + virtual string emitC() override { V3ERROR_NA_RETURN(""); } + virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + virtual bool cleanOut() const override { return true; } + virtual int instrCount() const override { return widthInstrs(); } + AstNode* defaultp() const { return op1p(); } + virtual bool same(const AstNode* samep) const override { return true; } +}; +class AstSetWildcard final : public AstNodeMath { + // Set a wildcard assoc array element and return object, '{} + // Parents: math + // Children: expression (elements or other queues) +public: + AstSetWildcard(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep) + : ASTGEN_SUPER_SetWildcard(fl) { + setOp1p(lhsp); + setNOp2p(keyp); + setOp3p(valuep); + } + ASTNODE_NODE_FUNCS(SetWildcard) + virtual string emitVerilog() override { return "'{}"; } + virtual string emitC() override { V3ERROR_NA_RETURN(""); } + virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + virtual bool cleanOut() const override { return true; } + virtual int instrCount() const override { return widthInstrs(); } + AstNode* lhsp() const { return op1p(); } + AstNode* keyp() const { return op2p(); } + AstNode* valuep() const { return op3p(); } + virtual bool same(const AstNode* samep) const override { return true; } +}; + class AstConsDynArray final : public AstNodeMath { // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs} // Parents: math diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 613b681d7..eaf7c1f39 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -88,6 +88,7 @@ private: if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths! || VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays + || VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType) || VN_IS(nodep->dtypep()->skipRefp(), QueueDType) diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index ae3f8ce03..7117fee7e 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -611,6 +611,17 @@ void EmitCFunc::emitVarReset(AstVar* varp) { emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")", VN_AS(valuep, Const)); } + } else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) { + if (initarp->defaultp()) { + emitSetVarConstant(varNameProtected + ".atDefault()", + VN_AS(initarp->defaultp(), Const)); + } + const auto& mapr = initarp->map(); + for (const auto& itr : mapr) { + AstNode* const valuep = itr.second->valuep(); + emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")", + VN_AS(valuep, Const)); + } } else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { if (initarp->defaultp()) { puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst())); @@ -642,6 +653,11 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1, suffix + ".atDefault()" + cvtarray); + } else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) { + // Access std::array as C array + const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); + return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1, + suffix + ".atDefault()" + cvtarray); } else if (VN_IS(dtypep, ClassRefDType)) { return ""; // Constructor does it } else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 629b90397..31d1f5a11 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -379,6 +379,14 @@ public: } puts(")"); } + virtual void visit(AstWildcardSel* nodep) override { + iterateAndNextNull(nodep->fromp()); + putbs(".at("); + AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType); + UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type"); + iterateAndNextNull(nodep->bitp()); + puts(")"); + } virtual void visit(AstCCall* nodep) override { const AstCFunc* const funcp = nodep->funcp(); const AstNodeModule* const funcModp = EmitCParentModule::get(funcp); @@ -1189,6 +1197,24 @@ public: iterateAndNextNull(nodep->valuep()); puts(")"); } + virtual void visit(AstConsWildcard* nodep) override { + putbs(nodep->dtypep()->cType("", false, false)); + puts("()"); + if (nodep->defaultp()) { + putbs(".setDefault("); + iterateAndNextNull(nodep->defaultp()); + puts(")"); + } + } + virtual void visit(AstSetWildcard* nodep) override { + iterateAndNextNull(nodep->lhsp()); + putbs(".set("); + iterateAndNextNull(nodep->keyp()); + puts(", "); + putbs(""); + iterateAndNextNull(nodep->valuep()); + puts(")"); + } virtual void visit(AstConsDynArray* nodep) override { putbs(nodep->dtypep()->cType("", false, false)); if (!nodep->lhsp()) { diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 4b0cbbbba..ba59ea462 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -133,6 +133,11 @@ private: iterateNull(nodep->virtRefDTypep()); }); } + virtual void visit(AstWildcardArrayDType* nodep) override { + m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { // + iterateNull(nodep->virtRefDTypep()); + }); + } virtual void visit(AstBasicDType* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { m_hash += nodep->keyword(); diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index 2cc65af6a..f2d57f289 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -138,6 +138,8 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nra AstNode* const keyp = arangep->elementsp()->unlinkFrBack(); arrayp = new AstBracketArrayDType(nrangep->fileline(), VFlagChildDType(), arrayp, keyp); + } else if (VN_IS(nrangep, WildcardRange)) { + arrayp = new AstWildcardArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp}; } else { UASSERT_OBJ(0, nrangep, "Expected range or unsized range"); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8a833d9f2..c98e856aa 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -981,6 +981,27 @@ private: } } + virtual void visit(AstWildcardSel* nodep) override { + // Signed/Real: Output type based on array-declared type; binary operator + if (m_vup->prelim()) { + const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp(); + const AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType); + if (!adtypep) { + UINFO(1, " Related dtype: " << fromDtp << endl); + nodep->v3fatalSrc("Wildcard array reference is not to wildcard array"); + } + const AstBasicDType* const basicp = nodep->bitp()->dtypep()->skipRefp()->basicp(); + if (!basicp + || (basicp->keyword() != VBasicDTypeKwd::STRING + && !basicp->keyword().isIntNumeric())) { + nodep->v3error("Wildcard index must be integral (IEEE 1800-2017 7.8.1)"); + } + iterateCheckTyped(nodep, "Wildcard associative select", nodep->bitp(), + adtypep->findStringDType(), BOTH); + nodep->dtypeFrom(adtypep->subDTypep()); + } + } + virtual void visit(AstSliceSel* nodep) override { // Always creates as output an unpacked array if (m_vup->prelim()) { @@ -1582,6 +1603,14 @@ private: nodep->dtypep(nodep); // The array itself, not subDtype UINFO(4, "dtWidthed " << nodep << endl); } + virtual void visit(AstWildcardArrayDType* nodep) override { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); + // Cleanup array size + nodep->dtypep(nodep); // The array itself, not subDtype + UINFO(4, "dtWidthed " << nodep << endl); + } virtual void visit(AstBasicDType* nodep) override { if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed if (nodep->generic()) return; // Already perfect @@ -2178,6 +2207,31 @@ private: EXTEND_EXP); } } + virtual void visit(AstConsWildcard* nodep) override { + // Type computed when constructed here + auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType); + UASSERT_OBJ(vdtypep, nodep, "ConsWildcard requires wildcard upper parent data type"); + if (m_vup->prelim()) { + nodep->dtypeFrom(vdtypep); + if (nodep->defaultp()) { + iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT, FINAL, + vdtypep->subDTypep(), EXTEND_EXP); + } + } + } + virtual void visit(AstSetWildcard* nodep) override { + // Type computed when constructed here + auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType); + UASSERT_OBJ(vdtypep, nodep, "SetWildcard requires wildcard upper parent data type"); + if (m_vup->prelim()) { + nodep->dtypeFrom(vdtypep); + userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p()); + iterateCheck(nodep, "key", nodep->keyp(), CONTEXT, FINAL, vdtypep->findStringDType(), + EXTEND_EXP); + iterateCheck(nodep, "value", nodep->valuep(), CONTEXT, FINAL, vdtypep->subDTypep(), + EXTEND_EXP); + } + } virtual void visit(AstConsDynArray* nodep) override { // Type computed when constructed here AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType); @@ -2426,6 +2480,7 @@ private: } } else if (VN_IS(fromDtp, EnumDType) // || VN_IS(fromDtp, AssocArrayDType) // + || VN_IS(fromDtp, WildcardArrayDType) // || VN_IS(fromDtp, UnpackArrayDType) // || VN_IS(fromDtp, DynArrayDType) // || VN_IS(fromDtp, QueueDType) // @@ -2523,6 +2578,8 @@ private: methodCallEnum(nodep, adtypep); } else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) { methodCallAssoc(nodep, adtypep); + } else if (AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType)) { + methodCallWildcard(nodep, adtypep); } else if (AstDynArrayDType* const adtypep = VN_CAST(fromDtp, DynArrayDType)) { methodCallDyn(nodep, adtypep); } else if (AstQueueDType* const adtypep = VN_CAST(fromDtp, QueueDType)) { @@ -2684,6 +2741,89 @@ private: nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ()); } } + void methodCallWildcard(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) { + AstCMethodHard* newp = nullptr; + if (nodep->name() == "num" // function int num() + || nodep->name() == "size") { + methodOkArguments(nodep, 0, 0); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + "size"}; // So don't need num() + newp->dtypeSetSigned32(); + } else if (nodep->name() == "first" // function int first(ref index) + || nodep->name() == "last" // + || nodep->name() == "next" // + || nodep->name() == "prev" // + || nodep->name() == "unique_index" // + || nodep->name() == "find_index" || nodep->name() == "find_first_index" + || nodep->name() == "find_last_index") { + nodep->v3error("Array method " << nodep->prettyNameQ() + << " not legal on wildcard associative arrays"); + } else if (nodep->name() == "exists") { // function int exists(input index) + // IEEE really should have made this a "bit" return + methodOkArguments(nodep, 1, 1); + AstNode* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists", + index_exprp->unlinkFrBack()}; + newp->dtypeSetSigned32(); + newp->pure(true); + } else if (nodep->name() == "delete") { // function void delete([input integer index]) + methodOkArguments(nodep, 0, 1); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); + if (!nodep->pinsp()) { + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + "clear"}; + newp->makeStatement(); + } else { + AstNode* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + "erase", index_exprp->unlinkFrBack()}; + newp->makeStatement(); + } + } else if (nodep->name() == "sort" || nodep->name() == "rsort" + || nodep->name() == "reverse" || nodep->name() == "shuffle") { + nodep->v3error("Array method " << nodep->prettyNameQ() + << " not legal on associative arrays"); + } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" + || nodep->name() == "sum" || nodep->name() == "product") { + // All value return + AstWith* const withp + = methodWithArgument(nodep, false, false, adtypep->subDTypep(), + adtypep->findStringDType(), adtypep->subDTypep()); + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + "r_" + nodep->name(), withp}; + newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep()); + if (!nodep->firstAbovep()) newp->makeStatement(); + } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique") { + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name()}; + newp->dtypeFrom(adtypep); + if (!nodep->firstAbovep()) newp->makeStatement(); + } else if (nodep->name() == "find" || nodep->name() == "find_first" + || nodep->name() == "find_last") { + AstWith* const withp + = methodWithArgument(nodep, true, false, nodep->findBitDType(), + adtypep->findStringDType(), adtypep->subDTypep()); + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), withp}; + newp->dtypeFrom(adtypep); + if (!nodep->firstAbovep()) newp->makeStatement(); + } else { + nodep->v3error("Unknown wildcard associative array method " << nodep->prettyNameQ()); + nodep->dtypeFrom(adtypep->subDTypep()); // Best guess + } + if (newp) { + newp->protect(false); + newp->didWidth(true); + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + } + } void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) { AstCMethodHard* newp = nullptr; if (nodep->name() == "num" // function int num() @@ -2789,6 +2929,13 @@ private: VL_DANGLING(index_exprp); // May have been edited return VN_AS(nodep->pinsp(), Arg)->exprp(); } + AstNode* methodCallWildcardIndexExpr(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) { + AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp(); + iterateCheck(nodep, "index", index_exprp, CONTEXT, FINAL, adtypep->findStringDType(), + EXTEND_EXP); + VL_DANGLING(index_exprp); // May have been edited + return VN_AS(nodep->pinsp(), Arg)->exprp(); + } void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) { if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) { varrefp->access(access); @@ -3393,6 +3540,8 @@ private: VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep); } else if (auto* const vdtypep = VN_CAST(dtypep, AssocArrayDType)) { VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep); + } else if (auto* const vdtypep = VN_CAST(dtypep, WildcardArrayDType)) { + VL_DO_DANGLING(patternWildcard(nodep, vdtypep, defaultp), nodep); } else if (auto* const vdtypep = VN_CAST(dtypep, DynArrayDType)) { VL_DO_DANGLING(patternDynArray(nodep, vdtypep, defaultp), nodep); } else if (auto* const vdtypep = VN_CAST(dtypep, QueueDType)) { @@ -3576,6 +3725,26 @@ private: // if (debug() >= 9) newp->dumpTree("-apat-out: "); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } + void patternWildcard(AstPattern* nodep, AstWildcardArrayDType* arrayDtp, + AstPatMember* defaultp) { + AstNode* defaultValuep = nullptr; + if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack(); + AstNode* newp = new AstConsWildcard{nodep->fileline(), defaultValuep}; + newp->dtypeFrom(arrayDtp); + for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp; + patp = VN_AS(patp->nextp(), PatMember)) { + patp->dtypep(arrayDtp->subDTypep()); + AstNode* const valuep = patternMemberValueIterate(patp); + AstNode* const keyp = patp->keyp(); + auto* const newap + = new AstSetWildcard{nodep->fileline(), newp, keyp->unlinkFrBack(), valuep}; + newap->dtypeFrom(arrayDtp); + newp = newap; + } + nodep->replaceWith(newp); + // if (debug() >= 9) newp->dumpTree("-apat-out: "); + VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present + } void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember*) { AstNode* newp = new AstConsDynArray(nodep->fileline()); newp->dtypeFrom(arrayp); @@ -4092,6 +4261,7 @@ private: added = true; newFormat += "%g"; } else if (VN_IS(dtypep, AssocArrayDType) // + || VN_IS(dtypep, WildcardArrayDType) // || VN_IS(dtypep, ClassRefDType) // || VN_IS(dtypep, DynArrayDType) // || VN_IS(dtypep, QueueDType)) { diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 793b98aec..4d6b7de01 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -88,6 +88,7 @@ private: if (const AstNodeArrayDType* const adtypep = VN_CAST(ddtypep, NodeArrayDType)) { fromRange = adtypep->declRange(); } else if (VN_IS(ddtypep, AssocArrayDType)) { + } else if (VN_IS(ddtypep, WildcardArrayDType)) { } else if (VN_IS(ddtypep, DynArrayDType)) { } else if (VN_IS(ddtypep, QueueDType)) { } else if (const AstNodeUOrStructDType* const adtypep @@ -257,6 +258,15 @@ private: if (debug() >= 9) newp->dumpTree(cout, "--SELBTn: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); + } else if (const AstWildcardArrayDType* const adtypep + = VN_CAST(ddtypep, WildcardArrayDType)) { + // SELBIT(array, index) -> WILDCARDSEL(array, index) + AstNode* const subp = rhsp; + AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, subp}; + newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference + if (debug() >= 9) newp->dumpTree(cout, "--SELBTn: "); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstDynArrayDType* const adtypep = VN_CAST(ddtypep, DynArrayDType)) { // SELBIT(array, index) -> CMETHODCALL(queue, "at", index) AstNode* const subp = rhsp; diff --git a/src/verilog.y b/src/verilog.y index 34fbe86b0..82faf4a7d 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2074,10 +2074,8 @@ variable_dimension: // ==IEEE: variable_dimension // // IEEE: associative_dimension (if data_type) // // Can't tell which until see if expr is data type or not | '[' exprOrDataType ']' { $$ = new AstBracketRange($1, $2); } - | yP_BRASTAR ']' - { $$ = nullptr; BBUNSUP($1, "Unsupported: [*] wildcard associative arrays"); } - | '[' '*' ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: [*] wildcard associative arrays"); } + | yP_BRASTAR ']' { $$ = new AstWildcardRange{$1}; } + | '[' '*' ']' { $$ = new AstWildcardRange{$1}; } // // IEEE: queue_dimension // // '[' '$' ']' -- $ is part of expr, see '[' constExpr ']' // // '[' '$' ':' expr ']' -- anyrange:expr:$ diff --git a/test_regress/t/t_assoc_wildcard.pl b/test_regress/t/t_assoc_wildcard.pl new file mode 100755 index 000000000..9a15dd2cc --- /dev/null +++ b/test_regress/t/t_assoc_wildcard.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_assoc_wildcard_unsup.v b/test_regress/t/t_assoc_wildcard.v similarity index 71% rename from test_regress/t/t_assoc_wildcard_unsup.v rename to test_regress/t/t_assoc_wildcard.v index d17eaf3c1..2d4e5c782 100644 --- a/test_regress/t/t_assoc_wildcard_unsup.v +++ b/test_regress/t/t_assoc_wildcard.v @@ -22,26 +22,25 @@ module t (/*AUTOARG*/ cyc <= cyc + 1; begin // Wildcard - string a [*]; + string a [*] = '{default: "nope", "BBBBB": "fooing", 23'h434343: "baring"}; int k; string v; + v = a["CCC"]; `checks(v, "baring"); + v = a["BBBBB"]; `checks(v, "fooing"); + a[32'd1234] = "fooed"; a[4'd3] = "bared"; - i = a.num(); `checkh(i, 2); - i = a.size(); `checkh(i, 2); - v = a[32'd1234]; `checks(v, "fooed"); + a[79'h4141] = "bazed"; + i = a.num(); `checkh(i, 5); + i = a.size(); `checkh(i, 5); + v = a[39'd1234]; `checks(v, "fooed"); + v = a["AA"]; `checks(v, "bazed"); v = a[4'd3]; `checks(v, "bared"); i = a.exists("baz"); `checkh(i, 0); i = a.exists(4'd3); `checkh(i, 1); - i = a.first(k); `checkh(i, 1); `checks(k, 4'd3); - i = a.next(k); `checkh(i, 1); `checks(k, 32'd1234); - i = a.next(k); `checkh(i, 0); - i = a.last(k); `checkh(i, 1); `checks(k, 32'd1234); - i = a.prev(k); `checkh(i, 1); `checks(k, 4'd3); - i = a.prev(k); `checkh(i, 0); a.delete(4'd3); - i = a.size(); `checkh(i, 1); + i = a.size(); `checkh(i, 4); end $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_assoc_wildcard_bad.out b/test_regress/t/t_assoc_wildcard_bad.out new file mode 100644 index 000000000..2bd459ff6 --- /dev/null +++ b/test_regress/t/t_assoc_wildcard_bad.out @@ -0,0 +1,73 @@ +%Error: t/t_assoc_wildcard_bad.v:23:13: The 1 arguments passed to .num method does not match its requiring 0 arguments + : ... In instance t + 23 | v = a.num("badarg"); + | ^~~ +%Error: t/t_assoc_wildcard_bad.v:24:13: The 1 arguments passed to .size method does not match its requiring 0 arguments + : ... In instance t + 24 | v = a.size("badarg"); + | ^~~~ +%Error: t/t_assoc_wildcard_bad.v:25:13: The 0 arguments passed to .exists method does not match its requiring 1 arguments + : ... In instance t + 25 | v = a.exists(); + | ^~~~~~ +%Error: t/t_assoc_wildcard_bad.v:26:13: The 2 arguments passed to .exists method does not match its requiring 1 arguments + : ... In instance t + 26 | v = a.exists(k, "bad2"); + | ^~~~~~ +%Error: t/t_assoc_wildcard_bad.v:27:9: The 2 arguments passed to .delete method does not match its requiring 0 to 1 arguments + : ... In instance t + 27 | a.delete(k, "bad2"); + | ^~~~~~ +%Error: t/t_assoc_wildcard_bad.v:29:9: Array method 'sort' not legal on associative arrays + : ... In instance t + 29 | a.sort; + | ^~~~ +%Error: t/t_assoc_wildcard_bad.v:30:9: Array method 'rsort' not legal on associative arrays + : ... In instance t + 30 | a.rsort; + | ^~~~~ +%Error: t/t_assoc_wildcard_bad.v:31:9: Array method 'reverse' not legal on associative arrays + : ... In instance t + 31 | a.reverse; + | ^~~~~~~ +%Error: t/t_assoc_wildcard_bad.v:32:9: Array method 'shuffle' not legal on associative arrays + : ... In instance t + 32 | a.shuffle; + | ^~~~~~~ +%Error: t/t_assoc_wildcard_bad.v:34:9: Array method 'first' not legal on wildcard associative arrays + : ... In instance t + 34 | a.first; + | ^~~~~ +%Error: t/t_assoc_wildcard_bad.v:35:9: Array method 'last' not legal on wildcard associative arrays + : ... In instance t + 35 | a.last; + | ^~~~ +%Error: t/t_assoc_wildcard_bad.v:36:9: Array method 'next' not legal on wildcard associative arrays + : ... In instance t + 36 | a.next; + | ^~~~ +%Error: t/t_assoc_wildcard_bad.v:37:9: Array method 'prev' not legal on wildcard associative arrays + : ... In instance t + 37 | a.prev; + | ^~~~ +%Error: t/t_assoc_wildcard_bad.v:38:9: Array method 'unique_index' not legal on wildcard associative arrays + : ... In instance t + 38 | a.unique_index; + | ^~~~~~~~~~~~ +%Error: t/t_assoc_wildcard_bad.v:39:9: Array method 'find_index' not legal on wildcard associative arrays + : ... In instance t + 39 | a.find_index; + | ^~~~~~~~~~ +%Error: t/t_assoc_wildcard_bad.v:40:9: Array method 'find_first_index' not legal on wildcard associative arrays + : ... In instance t + 40 | a.find_first_index; + | ^~~~~~~~~~~~~~~~ +%Error: t/t_assoc_wildcard_bad.v:41:9: Array method 'find_last_index' not legal on wildcard associative arrays + : ... In instance t + 41 | a.find_last_index; + | ^~~~~~~~~~~~~~~ +%Error: t/t_assoc_wildcard_bad.v:43:8: Wildcard index must be integral (IEEE 1800-2017 7.8.1) + : ... In instance t + 43 | a[x] = "bad"; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_assoc_wildcard_unsup.pl b/test_regress/t/t_assoc_wildcard_bad.pl similarity index 100% rename from test_regress/t/t_assoc_wildcard_unsup.pl rename to test_regress/t/t_assoc_wildcard_bad.pl diff --git a/test_regress/t/t_assoc_wildcard_bad.v b/test_regress/t/t_assoc_wildcard_bad.v new file mode 100644 index 000000000..85ebf83ba --- /dev/null +++ b/test_regress/t/t_assoc_wildcard_bad.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +typedef class Cls; + +class Cls; + integer imembera; + integer imemberb; +endclass : Cls + +module t (/*AUTOARG*/); + + initial begin + string a [*]; + string k; + string v; + + Cls x; + + v = a.num("badarg"); + v = a.size("badarg"); + v = a.exists(); // Bad + v = a.exists(k, "bad2"); + a.delete(k, "bad2"); + + a.sort; // Not legal on assoc + a.rsort; // Not legal on assoc + a.reverse; // Not legal on assoc + a.shuffle; // Not legal on assoc + + a.first; // Not legal on wildcard + a.last; // Not legal on wildcard + a.next; // Not legal on wildcard + a.prev; // Not legal on wildcard + a.unique_index; // Not legal on wildcard + a.find_index; // Not legal on wildcard + a.find_first_index; // Not legal on wildcard + a.find_last_index; // Not legal on wildcard + + a[x] = "bad"; + end +endmodule diff --git a/test_regress/t/t_assoc_wildcard_method.pl b/test_regress/t/t_assoc_wildcard_method.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_assoc_wildcard_method.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_assoc_wildcard_method.v b/test_regress/t/t_assoc_wildcard_method.v new file mode 100644 index 000000000..c61086e7b --- /dev/null +++ b/test_regress/t/t_assoc_wildcard_method.v @@ -0,0 +1,127 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t (/*AUTOARG*/); + initial begin + int q[*]; + int qe[*]; // Empty + int qv[$]; // Value returns + int qi[$]; // Index returns + int i; + string v; + + q = '{"a":1, "b":2, "c":2, "d":4, "e":3}; + v = $sformatf("%p", q); `checks(v, "'{\"a\":'h1, \"b\":'h2, \"c\":'h2, \"d\":'h4, \"e\":'h3} "); + + // NOT tested: with ... selectors + + //q.sort; // Not legal on assoc - see t_assoc_meth_bad + //q.rsort; // Not legal on assoc - see t_assoc_meth_bad + //q.reverse; // Not legal on assoc - see t_assoc_meth_bad + //q.shuffle; // Not legal on assoc - see t_assoc_meth_bad + + v = $sformatf("%p", qe); `checks(v, "'{}"); + qv = q.unique; + v = $sformatf("%p", qv); `checks(v, "'{'h1, 'h2, 'h4, 'h3} "); + qv = qe.unique; + v = $sformatf("%p", qv); `checks(v, "'{}"); + + //q.unique_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad + + // These require an with clause or are illegal + qv = q.find with (item == 2); + v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h2} "); + qv = q.find_first with (item == 2); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); + qv = q.find_last with (item == 2); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); + + qv = q.find with (item == 20); + v = $sformatf("%p", qv); `checks(v, "'{}"); + qv = q.find_first with (item == 20); + v = $sformatf("%p", qv); `checks(v, "'{}"); + qv = q.find_last with (item == 20); + v = $sformatf("%p", qv); `checks(v, "'{}"); + + //q.find_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad + //q.find_first_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad + //q.find_last_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad + + qv = q.min; + v = $sformatf("%p", qv); `checks(v, "'{'h1} "); + qv = q.max; + v = $sformatf("%p", qv); `checks(v, "'{'h4} "); + + qv = qe.min; + v = $sformatf("%p", qv); `checks(v, "'{}"); + qv = qe.max; + v = $sformatf("%p", qv); `checks(v, "'{}"); + + // Reduction methods + + i = q.sum; + `checkh(i, 32'hc); + i = q.sum with (item + 1); + `checkh(i, 32'h11); + i = q.product; + `checkh(i, 32'h30); + i = q.product with (item + 1); + `checkh(i, 32'h168); + + i = qe.sum; + `checkh(i, 32'h0); + i = qe.product; + `checkh(i, 32'h0); + + q = '{10:32'b1100, 11:32'b1010}; + i = q.and; + `checkh(i, 32'b1000); + i = q.and with (item + 1); + `checkh(i, 32'b1001); + i = q.or; + `checkh(i, 32'b1110); + i = q.or with (item + 1); + `checkh(i, 32'b1111); + i = q.xor; + `checkh(i, 32'b0110); + i = q.xor with (item + 1); + `checkh(i, 32'b0110); + + i = qe.and; + `checkh(i, 32'b0); + i = qe.or; + `checkh(i, 32'b0); + i = qe.xor; + `checkh(i, 32'b0); + + i = q.and(); + `checkh(i, 32'b1000); + i = q.and() with (item + 1); + `checkh(i, 32'b1001); + i = q.or(); + `checkh(i, 32'b1110); + i = q.or() with (item + 1); + `checkh(i, 32'b1111); + i = q.xor(); + `checkh(i, 32'b0110); + i = q.xor() with (item + 1); + `checkh(i, 32'b0110); + + i = qe.and(); + `checkh(i, 32'b0); + i = qe.or(); + `checkh(i, 32'b0); + i = qe.xor(); + `checkh(i, 32'b0); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_assoc_wildcard_unsup.out b/test_regress/t/t_assoc_wildcard_unsup.out deleted file mode 100644 index 209295f7f..000000000 --- a/test_regress/t/t_assoc_wildcard_unsup.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_assoc_wildcard_unsup.v:25:19: Unsupported: [*] wildcard associative arrays - 25 | string a [*]; - | ^~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to