From 2aedc91151715cc22dc19cab8611bcfb8a2dcde0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 1 Nov 2020 10:56:07 -0500 Subject: [PATCH] Support queue and associative array 'with' statements. (#2616) --- Changes | 2 + include/verilated_heavy.h | 314 +++++++++++++++++++++++++- src/V3Ast.cpp | 3 + src/V3Ast.h | 8 +- src/V3AstNodes.cpp | 9 + src/V3AstNodes.h | 41 +++- src/V3Clean.cpp | 5 + src/V3EmitC.cpp | 12 + src/V3LinkDot.cpp | 27 ++- src/V3Width.cpp | 228 ++++++++++++++++--- test_regress/t/t_array_method.out | 12 - test_regress/t/t_assoc_method.out | 153 ------------- test_regress/t/t_assoc_method.pl | 4 +- test_regress/t/t_assoc_method.v | 71 +++--- test_regress/t/t_dynarray_method.pl | 21 ++ test_regress/t/t_dynarray_method.v | 168 ++++++++++++++ test_regress/t/t_queue_method.out | 201 ----------------- test_regress/t/t_queue_method.pl | 4 +- test_regress/t/t_queue_method.v | 16 ++ test_regress/t/t_queue_method_bad.out | 38 ++-- test_regress/t/t_with.out | 41 ---- test_regress/t/t_with.pl | 4 +- test_regress/t/t_with.v | 10 +- test_regress/t/t_with_suggest_bad.out | 9 + test_regress/t/t_with_suggest_bad.pl | 20 ++ test_regress/t/t_with_suggest_bad.v | 21 ++ 26 files changed, 935 insertions(+), 507 deletions(-) delete mode 100644 test_regress/t/t_assoc_method.out create mode 100755 test_regress/t/t_dynarray_method.pl create mode 100644 test_regress/t/t_dynarray_method.v delete mode 100644 test_regress/t/t_queue_method.out delete mode 100644 test_regress/t/t_with.out create mode 100644 test_regress/t/t_with_suggest_bad.out create mode 100755 test_regress/t/t_with_suggest_bad.pl create mode 100644 test_regress/t/t_with_suggest_bad.v diff --git a/Changes b/Changes index 3dc21bc38..97ea9ae11 100644 --- a/Changes +++ b/Changes @@ -4,6 +4,8 @@ The contributors that suggested a given feature are shown in []. Thanks! * Verilator 4.103 devel +**** Support queue and associative array 'with' statements. (#2616) + **** Support queue slicing (#2326). **** Support associative array pattern assignments and defaults. diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index e54a77442..9e23181a8 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -26,9 +26,11 @@ #include "verilated.h" +#include #include #include #include +#include #include //=================================================================== @@ -230,6 +232,162 @@ public: const_iterator begin() const { return m_deque.begin(); } const_iterator end() const { return m_deque.end(); } + // Methods + void sort() { std::sort(m_deque.begin(), m_deque.end()); } + template void sort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + std::sort(m_deque.begin(), m_deque.end(), + [=](const T_Value& a, const T_Value& b) { return with_func(a) < with_func(b); }); + } + void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); } + template void rsort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + std::sort(m_deque.rbegin(), m_deque.rend(), + [=](const T_Value& a, const T_Value& b) { return with_func(a) < with_func(b); }); + } + void reverse() { std::reverse(m_deque.begin(), m_deque.end()); } + void shuffle() { + std::random_shuffle(m_deque.begin(), m_deque.end(), + [=](int) { return VL_RANDOM_I(32) % m_deque.size(); }); + } + VlQueue unique() const { + VlQueue out; + std::set saw; + for (const auto& i : m_deque) { + auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(i); + } + } + return out; + } + VlQueue unique_index() const { + VlQueue out; + IData index = 0; + std::set saw; + for (const auto& i : m_deque) { + auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(index); + } + ++index; + } + return out; + } + template VlQueue find(Func with_func) const { + VlQueue out; + for (const auto& i : m_deque) + if (with_func(i)) out.push_back(i); + return out; + } + template VlQueue find_index(Func with_func) const { + VlQueue out; + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(i)) out.push_back(index); + ++index; + } + return out; + } + template VlQueue find_first(Func with_func) const { + const auto it = std::find_if(m_deque.begin(), m_deque.end(), with_func); + if (it == m_deque.end()) return VlQueue{}; + return VlQueue::cons(*it); + } + template VlQueue find_first_index(Func with_func) const { + const auto it = std::find_if(m_deque.begin(), m_deque.end(), with_func); + if (it == m_deque.end()) return VlQueue{}; + return VlQueue::cons(std::distance(m_deque.begin(), it)); + } + template VlQueue find_last(Func with_func) const { + const auto it = std::find_if(m_deque.rbegin(), m_deque.rend(), with_func); + if (it == m_deque.rend()) return VlQueue{}; + return VlQueue::cons(*it); + } + template VlQueue find_last_index(Func with_func) const { + const auto it = std::find_if(m_deque.rbegin(), m_deque.rend(), with_func); + if (it == m_deque.rend()) return VlQueue{}; + // Return index must be relative to beginning + return VlQueue::cons(m_deque.size() - 1 - std::distance(m_deque.rbegin(), it)); + } + + // Reduction operators + VlQueue min() const { + if (m_deque.empty()) return VlQueue(); + const auto it = std::min_element(m_deque.begin(), m_deque.end()); + return VlQueue::cons(*it); + } + VlQueue max() const { + if (m_deque.empty()) return VlQueue(); + const auto it = std::max_element(m_deque.begin(), m_deque.end()); + return VlQueue::cons(*it); + } + + T_Value r_sum() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out += i; + return out; + } + template T_Value r_sum(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out += with_func(i); + return out; + } + T_Value r_product() const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{*it}; + ++it; + for (; it != m_deque.end(); ++it) out *= *it; + return out; + } + template T_Value r_product(Func with_func) const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{with_func(*it)}; + ++it; + for (; it != m_deque.end(); ++it) out *= with_func(*it); + return out; + } + T_Value r_and() const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{*it}; + ++it; + for (; it != m_deque.end(); ++it) out &= *it; + return out; + } + template T_Value r_and(Func with_func) const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{with_func(*it)}; + ++it; + for (; it != m_deque.end(); ++it) out &= with_func(*it); + return out; + } + T_Value r_or() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out |= i; + return out; + } + template T_Value r_or(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out |= with_func(i); + return out; + } + T_Value r_xor() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out ^= i; + return out; + } + template T_Value r_xor(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out ^= with_func(i); + return out; + } + // Dumping. Verilog: str = $sformatf("%p", assoc) std::string to_string() const { if (m_deque.empty()) return "'{}"; // No trailing space @@ -355,7 +513,8 @@ public: T_Value& at(const T_Key& index) { const auto it = m_map.find(index); if (it == m_map.end()) { - const auto pit = m_map.insert(std::make_pair(index, m_defaultValue)); + std::pair pit + = m_map.insert(std::make_pair(index, m_defaultValue)); return pit.first->second; } return it->second; @@ -383,6 +542,159 @@ public: const_iterator begin() const { return m_map.begin(); } const_iterator end() const { return m_map.end(); } + // Methods + VlQueue unique() const { + VlQueue out; + std::set saw; + for (const auto& i : m_map) { + auto it = saw.find(i.second); + if (it == saw.end()) { + saw.insert(it, i.second); + out.push_back(i.second); + } + } + return out; + } + VlQueue unique_index() const { + VlQueue out; + std::set saw; + for (const auto& i : m_map) { + auto it = saw.find(i.second); + if (it == saw.end()) { + saw.insert(it, i.second); + out.push_back(i.first); + } + } + return out; + } + template VlQueue find(Func with_func) const { + VlQueue out; + for (const auto& i : m_map) + if (with_func(i.second)) out.push_back(i.second); + return out; + } + template VlQueue find_index(Func with_func) const { + VlQueue out; + for (const auto& i : m_map) + if (with_func(i.second)) out.push_back(i.first); + return out; + } + template VlQueue find_first(Func with_func) const { + const auto it + = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { + return with_func(i.second); + }); + if (it == m_map.end()) return VlQueue{}; + return VlQueue::cons(it->second); + } + template VlQueue find_first_index(Func with_func) const { + const auto it + = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { + return with_func(i.second); + }); + if (it == m_map.end()) return VlQueue{}; + return VlQueue::cons(it->first); + } + template VlQueue find_last(Func with_func) const { + const auto it + = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { + return with_func(i.second); + }); + if (it == m_map.rend()) return VlQueue{}; + return VlQueue::cons(it->second); + } + template VlQueue find_last_index(Func with_func) const { + const auto it + = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { + return with_func(i.second); + }); + if (it == m_map.rend()) return VlQueue{}; + return VlQueue::cons(it->first); + } + + // Reduction operators + VlQueue min() const { + if (m_map.empty()) return VlQueue(); + const auto it = std::min_element( + m_map.begin(), m_map.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + return VlQueue::cons(it->second); + } + VlQueue max() const { + if (m_map.empty()) return VlQueue(); + const auto it = std::max_element( + m_map.begin(), m_map.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + return VlQueue::cons(it->second); + } + + T_Value r_sum() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out += i.second; + return out; + } + template T_Value r_sum(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out += with_func(i.second); + return out; + } + T_Value r_product() const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{it->second}; + ++it; + for (; it != m_map.end(); ++it) out *= it->second; + return out; + } + template T_Value r_product(Func with_func) const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{with_func(it->second)}; + ++it; + for (; it != m_map.end(); ++it) out *= with_func(it->second); + return out; + } + T_Value r_and() const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{it->second}; + ++it; + for (; it != m_map.end(); ++it) out &= it->second; + return out; + } + template T_Value r_and(Func with_func) const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{with_func(it->second)}; + ++it; + for (; it != m_map.end(); ++it) out &= with_func(it->second); + return out; + } + T_Value r_or() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out |= i.second; + return out; + } + template T_Value r_or(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out |= with_func(i.second); + return out; + } + T_Value r_xor() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out ^= i.second; + return out; + } + template T_Value r_xor(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out ^= with_func(i.second); + return out; + } + // Dumping. Verilog: str = $sformatf("%p", assoc) std::string to_string() const { if (m_map.empty()) return "'{}"; // No trailing space diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index e8f8682ef..6444ae0e4 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1261,6 +1261,9 @@ AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { AstNodeDType* AstNode::findVoidDType() const { return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); } +AstNodeDType* AstNode::findQueueIndexDType() const { + return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline()); +} //###################################################################### // AstNVisitor diff --git a/src/V3Ast.h b/src/V3Ast.h index eed8966f7..ab8baf611 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -696,8 +696,7 @@ public: STMTTEMP, XTEMP, IFACEREF, // Used to link Interfaces between modules - MEMBER, - WITH + MEMBER }; enum en m_e; inline AstVarType() @@ -712,7 +711,7 @@ public: static const char* const names[] = { "?", "GPARAM", "LPARAM", "GENVAR", "VAR", "SUPPLY0", "SUPPLY1", "WIRE", "WREAL", "IMPLICITWIRE", "TRIWIRE", "TRI0", "TRI1", "PORT", - "BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER", "WITH"}; + "BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"}; return names[m_e]; } bool isSignal() const { @@ -728,7 +727,7 @@ public: bool isProcAssignable() const { return (m_e == GPARAM || m_e == LPARAM || m_e == GENVAR || m_e == VAR || m_e == BLOCKTEMP || m_e == MODULETEMP || m_e == STMTTEMP || m_e == XTEMP || m_e == IFACEREF - || m_e == MEMBER || m_e == WITH); + || m_e == MEMBER); } bool isTemp() const { return (m_e == BLOCKTEMP || m_e == MODULETEMP || m_e == STMTTEMP || m_e == XTEMP); @@ -1736,6 +1735,7 @@ public: AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } AstNodeDType* findVoidDType() const; + AstNodeDType* findQueueIndexDType() const; AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findLogicRangeDType(const VNumRange& range, int widthMin, diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index acd24f932..f6c61dcc0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -875,6 +875,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { return m_voidp; } +AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) { + if (VL_UNLIKELY(!m_queueIndexp)) { + AstQueueDType* newp = new AstQueueDType(fl, AstNode::findUInt32DType(), nullptr); + addTypesp(newp); + m_queueIndexp = newp; + } + return m_queueIndexp; +} + AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) { if (m_basicps[kwd]) return m_basicps[kwd]; // diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index da115b3e2..4712c7d31 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1100,6 +1100,12 @@ public: refDTypep(nullptr); dtypep(nullptr); // V3Width will resolve } + AstQueueDType(FileLine* fl, AstNodeDType* dtp, AstNode* boundp) + : ASTGEN_SUPER(fl) { + setNOp2p(boundp); + refDTypep(dtp); + dtypep(dtp); + } ASTNODE_NODE_FUNCS(QueueDType) virtual const char* broken() const override { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) @@ -3091,6 +3097,29 @@ public: AstNode* exprp() const { return op2p(); } }; +class AstLambdaArgRef : public AstNodeMath { + // Lambda argument usage + // These are not AstVarRefs because we need to be able to delete/clone lambdas during + // optimizations and AstVar's are painful to remove. +private: + string m_name; // Name of variable + +public: + AstLambdaArgRef(FileLine* fl, const string& name) + : ASTGEN_SUPER(fl) + , m_name{name} {} + ASTNODE_NODE_FUNCS(LambdaArgRef) + virtual V3Hash sameHash() const override { return V3Hash(); } + virtual bool same(const AstNode* samep) const override { return true; } + virtual string emitVerilog() override { return name(); } + virtual string emitC() override { V3ERROR_NA_RETURN(""); } + virtual bool cleanOut() const { return true; } + virtual bool hasDType() const override { return true; } + virtual int instrCount() const override { return widthInstrs(); } + virtual string name() const override { return m_name; } // * = Var name + virtual void name(const string& name) override { m_name = name; } +}; + class AstWith : public AstNodeStmt { // Used as argument to method, then to AstCMethodHard // dtypep() contains the with lambda's return dtype @@ -3098,17 +3127,21 @@ class AstWith : public AstNodeStmt { // Children: VAR that declares the index variable // Children: math (equation establishing the with) public: - AstWith(FileLine* fl, AstVar* varp, AstNode* exprp) + AstWith(FileLine* fl, AstLambdaArgRef* argrefp, AstNode* exprp) : ASTGEN_SUPER(fl) { - addOp1p(varp); + addOp1p(argrefp); addNOp2p(exprp); } ASTNODE_NODE_FUNCS(With) virtual V3Hash sameHash() const override { return V3Hash(); } virtual bool same(const AstNode* samep) const override { return true; } virtual bool hasDType() const override { return true; } + virtual const char* broken() const override { + BROKEN_RTN(!argrefp()); // varp needed to know lambda's arg dtype + return nullptr; + } // - AstVar* varp() const { return VN_CAST(op1p(), Var); } + AstLambdaArgRef* argrefp() const { return VN_CAST(op1p(), LambdaArgRef); } AstNode* exprp() const { return op2p(); } }; @@ -9028,6 +9061,7 @@ class AstTypeTable : public AstNode { // Container for hash of standard data types // Children: NODEDTYPEs AstVoidDType* m_voidp = nullptr; + AstQueueDType* m_queueIndexp = nullptr; AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX]; // typedef std::map DetailedMap; @@ -9042,6 +9076,7 @@ public: AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType); } // op1 = List of dtypes void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); } AstVoidDType* findVoidDType(FileLine* fl); + AstQueueDType* findQueueIndexDType(FileLine* fl); AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd); AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width, int widthMin, VSigning numeric); diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index c67f18c1c..3dcd7fc1e 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -285,6 +285,11 @@ private: ensureCleanAndNext(nodep->pinsp()); setClean(nodep, true); } + virtual void visit(AstWith* nodep) override { + iterateChildren(nodep); + ensureCleanAndNext(nodep->exprp()); + setClean(nodep, true); + } virtual void visit(AstIntfRef* nodep) override { iterateChildren(nodep); setClean(nodep, true); // generates a string, so not relevant diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index c209d55da..b78c82715 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -425,6 +425,18 @@ public: UASSERT_OBJ(!nodep->isStatement() || VN_IS(nodep->dtypep(), VoidDType), nodep, "Statement of non-void data type"); } + virtual void visit(AstLambdaArgRef* nodep) override { putbs(nodep->nameProtect()); } + virtual void visit(AstWith* nodep) override { + // With uses a C++11 lambda + putbs("[=]("); + if (auto* argrefp = nodep->argrefp()) { + putbs(argrefp->dtypep()->cType(nodep->argrefp()->nameProtect(), false, false)); + } + // Probably fragile, V3Task may need to convert to a AstCReturn + puts(") { return "); + iterateAndNextNull(nodep->exprp()); + puts("; }\n"); + } virtual void visit(AstIntfRef* nodep) override { putsQuoted(VIdProtect::protectWordsIf(AstNode::vcdName(nodep->name()), nodep->protect())); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index c73104f5a..2f20432d4 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -90,7 +90,9 @@ public: }; class LinkNodeMatcherVar : public VNodeMatcher { public: - virtual bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, Var); } + virtual bool nodeMatch(const AstNode* nodep) const override { + return VN_IS(nodep, Var) || VN_IS(nodep, LambdaArgRef); + } }; class LinkNodeMatcherVarIO : public VNodeMatcher { public: @@ -1270,9 +1272,9 @@ class LinkDotFindVisitor : public AstNVisitor { VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp); } // Type depends on the method used, let V3Width figure it out later - auto* varp = new AstVar(argFl, AstVarType::WITH, name, VFlagChildDType(), - new AstParseTypeDType(nodep->fileline())); - auto* newp = new AstWith(nodep->fileline(), varp, nodep->exprp()->unlinkFrBackWithNext()); + auto* argrefp = new AstLambdaArgRef(argFl, name); + auto* newp + = new AstWith(nodep->fileline(), argrefp, nodep->exprp()->unlinkFrBackWithNext()); funcrefp->addPinsp(newp); nodep->replaceWith(funcrefp->unlinkFrBack()); VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -1287,7 +1289,9 @@ class LinkDotFindVisitor : public AstNVisitor { m_curSymp = m_statep->insertBlock(m_curSymp, "__Vwith" + cvtToStr(m_modWithNum), nodep, m_packagep); m_curSymp->fallbackp(oldCurSymp); - iterateChildren(nodep); + UASSERT_OBJ(nodep->argrefp(), nodep, "Missing lambda argref"); + // Insert argref's name into symbol table + m_statep->insertSym(m_curSymp, nodep->argrefp()->name(), nodep->argrefp(), nullptr); } } @@ -1539,6 +1543,11 @@ class LinkDotScopeVisitor : public AstNVisitor { symp->fallbackp(m_modSymp); // No recursion, we don't want to pick up variables } + virtual void visit(AstWith* nodep) override { + VSymEnt* symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, nullptr); + symp->fallbackp(m_modSymp); + // No recursion, we don't want to pick up variables + } virtual void visit(AstAssignAlias* nodep) override { // Track aliases created by V3Inline; if we get a VARXREF(aliased_from) // we'll need to replace it with a VARXREF(aliased_to) @@ -2279,6 +2288,14 @@ private: ok = true; m_ds.m_dotText = ""; } + } else if (AstLambdaArgRef* argrefp = VN_CAST(foundp->nodep(), LambdaArgRef)) { + if (allowVar) { + AstNode* newp = new AstLambdaArgRef(nodep->fileline(), argrefp->name()); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + ok = true; + m_ds.m_dotText = ""; + } } // if (!ok) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 3fd5a7ebd..a56889287 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -193,6 +193,7 @@ private: // TYPES typedef std::map, AstVar*> TableMap; typedef std::map PatVecMap; + typedef std::map DTypeQMap; // STATE WidthVP* m_vup = nullptr; // Current node state @@ -201,11 +202,13 @@ private: = nullptr; // Range for arrayed instantiations, nullptr for normal instantiations AstNodeFTask* m_ftaskp = nullptr; // Current function/task AstNodeProcedure* m_procedurep = nullptr; // Current final/always + AstLambdaArgRef* m_lambdaArgRefp = nullptr; // Argument to above lambda AstFunc* m_funcp = nullptr; // Current function AstAttrOf* m_attrp = nullptr; // Current attribute bool m_doGenerate; // Do errors later inside generate statement int m_dtTables = 0; // Number of created data type tables TableMap m_tableMap; // Created tables so can remove duplicates + DTypeQMap m_queueDTypeIndexed; // Queues with given index type // ENUMS enum ExtendRule : uint8_t { @@ -2322,6 +2325,7 @@ private: AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); AstBasicDType* basicp = fromDtp ? fromDtp->basicp() : nullptr; UINFO(9, " from dt " << fromDtp << endl); + userIterateAndNext(fromDtp, WidthVP(SELF, BOTH).p()); if (AstEnumDType* adtypep = VN_CAST(fromDtp, EnumDType)) { methodCallEnum(nodep, adtypep); } else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) { @@ -2345,6 +2349,20 @@ private: << nodep->fromp()->dtypep()->prettyTypeName() << "'"); } } + AstWith* methodWithArgument(AstMethodCall* nodep, bool required, bool arbReturn, + AstNodeDType* returnDtp, AstNodeDType* argDtp) { + UASSERT_OBJ(arbReturn || returnDtp, nodep, "Null return type"); + if (AstWith* withp = VN_CAST(nodep->pinsp(), With)) { + withp->argrefp()->dtypep(argDtp); + userIterate(withp, WidthVP(returnDtp, BOTH).p()); + withp->unlinkFrBack(); + return withp; + } else if (required) { + nodep->v3error("'with' statement is required for ." << nodep->prettyName() + << " method"); + } + return nullptr; + } void methodOkArguments(AstMethodCall* nodep, int minArg, int maxArg) { int narg = 0; for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) { @@ -2490,8 +2508,6 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size", nullptr); // So don't need num() newp->dtypeSetSigned32(); - newp->didWidth(true); - newp->protect(false); } else if (nodep->name() == "first" // function int first(ref index) || nodep->name() == "last" // || nodep->name() == "next" // @@ -2502,8 +2518,6 @@ private: nodep->name(), // first/last/next/prev index_exprp->unlinkFrBack()); newp->dtypeSetSigned32(); - newp->protect(false); - newp->didWidth(true); } else if (nodep->name() == "exists") { // function int exists(input index) // IEEE really should have made this a "bit" return methodOkArguments(nodep, 1, 1); @@ -2512,32 +2526,72 @@ private: index_exprp->unlinkFrBack()); newp->dtypeSetSigned32(); newp->pure(true); - newp->protect(false); - newp->didWidth(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", nullptr); - newp->protect(false); newp->makeStatement(); } else { AstNode* index_exprp = methodCallAssocIndexExpr(nodep, adtypep); newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "erase", index_exprp->unlinkFrBack()); - newp->protect(false); 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* withp = methodWithArgument(nodep, false, false, adtypep->subDTypep(), + 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(adtypep->subDTypep()); + if (!nodep->firstAbovep()) { newp->makeStatement(); } + } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" + || nodep->name() == "unique_index") { + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), nullptr); + if (nodep->name() == "unique_index") { + newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep())); + } else { + newp->dtypeFrom(adtypep); + } + if (!nodep->firstAbovep()) newp->makeStatement(); + } else if (nodep->name() == "find" || nodep->name() == "find_first" + || nodep->name() == "find_last") { + AstWith* withp = methodWithArgument(nodep, true, false, nodep->findLogicBoolDType(), + 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 if (nodep->name() == "find_index" || nodep->name() == "find_first_index" + || nodep->name() == "find_last_index") { + AstWith* withp = methodWithArgument(nodep, true, false, nodep->findLogicBoolDType(), + adtypep->subDTypep()); + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), withp); + newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep())); + if (!nodep->firstAbovep()) newp->makeStatement(); } else { nodep->v3error("Unknown built-in 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); @@ -2571,27 +2625,78 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at", nullptr); newp->dtypeFrom(adtypep->subDTypep()); - newp->protect(false); - newp->didWidth(true); } else if (nodep->name() == "size") { methodOkArguments(nodep, 0, 0); newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size", nullptr); newp->dtypeSetSigned32(); - newp->didWidth(true); - newp->protect(false); } else if (nodep->name() == "delete") { // function void delete() methodOkArguments(nodep, 0, 0); methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "clear", nullptr); newp->makeStatement(); + } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" + || nodep->name() == "sum" || nodep->name() == "product") { + // All value return + AstWith* withp = methodWithArgument(nodep, false, false, adtypep->subDTypep(), + 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(adtypep->subDTypep()); + if (!nodep->firstAbovep()) { newp->makeStatement(); } + } else if (nodep->name() == "reverse" || nodep->name() == "shuffle" + || nodep->name() == "sort" || nodep->name() == "rsort") { + AstWith* withp = NULL; + if (nodep->name() == "sort" || nodep->name() == "rsort") { + withp = methodWithArgument(nodep, false, true, nullptr, adtypep->subDTypep()); + } + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), withp); + newp->makeStatement(); + } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" + || nodep->name() == "unique_index") { + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), nullptr); + if (nodep->name() == "unique_index") { + newp->dtypep(newp->findQueueIndexDType()); + } else { + newp->dtypeFrom(adtypep); + } + if (!nodep->firstAbovep()) newp->makeStatement(); + } else if (nodep->name() == "find" || nodep->name() == "find_first" + || nodep->name() == "find_last" || nodep->name() == "find_index") { + AstWith* withp = methodWithArgument(nodep, true, false, nodep->findLogicBoolDType(), + 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 if (nodep->name() == "find_index" || nodep->name() == "find_first_index" + || nodep->name() == "find_last_index") { + AstWith* withp = methodWithArgument(nodep, true, false, nodep->findLogicBoolDType(), + adtypep->subDTypep()); + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), withp); + newp->dtypep(newp->findQueueIndexDType()); + if (!nodep->firstAbovep()) newp->makeStatement(); } else { nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in dynamic 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); @@ -2605,23 +2710,18 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at", nullptr); newp->dtypeFrom(adtypep->subDTypep()); - newp->protect(false); - newp->didWidth(true); } else if (nodep->name() == "num" // function int num() || nodep->name() == "size") { methodOkArguments(nodep, 0, 0); newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size", nullptr); newp->dtypeSetSigned32(); - newp->didWidth(true); - newp->protect(false); } 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", nullptr); - newp->protect(false); newp->makeStatement(); } else { AstNode* index_exprp = methodCallQueueIndexExpr(nodep); @@ -2629,14 +2729,10 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "pop_front", nullptr); newp->dtypeFrom(adtypep->subDTypep()); - newp->protect(false); - newp->didWidth(true); newp->makeStatement(); } else { newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "erase", index_exprp->unlinkFrBack()); - newp->protect(false); - newp->didWidth(true); newp->makeStatement(); } } @@ -2649,13 +2745,11 @@ private: if (index_exprp->isZero()) { // insert(0, ...) is a push_front newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), "push_front", argp->exprp()->unlinkFrBack()); - newp->protect(false); newp->makeStatement(); } else { newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), index_exprp->unlinkFrBack()); newp->addPinsp(argp->exprp()->unlinkFrBack()); - newp->protect(false); newp->makeStatement(); } } else if (nodep->name() == "pop_front" || nodep->name() == "pop_back") { @@ -2664,8 +2758,6 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), nullptr); newp->dtypeFrom(adtypep->subDTypep()); - newp->protect(false); - newp->didWidth(true); if (!nodep->firstAbovep()) { newp->makeStatement(); } } else if (nodep->name() == "push_back" || nodep->name() == "push_front") { methodOkArguments(nodep, 1, 1); @@ -2674,14 +2766,67 @@ private: iterateCheckTyped(nodep, "push value", argp->exprp(), adtypep->subDTypep(), BOTH); newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), argp->exprp()->unlinkFrBack()); - newp->protect(false); newp->makeStatement(); + } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor" + || nodep->name() == "sum" || nodep->name() == "product") { + AstWith* withp = methodWithArgument(nodep, false, false, adtypep->subDTypep(), + 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(adtypep->subDTypep()); + if (!nodep->firstAbovep()) { newp->makeStatement(); } + } else if (nodep->name() == "reverse" || nodep->name() == "shuffle" + || nodep->name() == "sort" || nodep->name() == "rsort") { + AstWith* withp = NULL; + if (nodep->name() == "sort" || nodep->name() == "rsort") { + withp = methodWithArgument(nodep, false, true, nullptr, adtypep->subDTypep()); + } + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), withp); + newp->makeStatement(); + } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" + || nodep->name() == "unique_index") { + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), nullptr); + if (nodep->name() == "unique_index") { + newp->dtypep(newp->findQueueIndexDType()); + } else { + newp->dtypeFrom(adtypep); + } + if (!nodep->firstAbovep()) newp->makeStatement(); + } else if (nodep->name() == "find" || nodep->name() == "find_first" + || nodep->name() == "find_last") { + AstWith* withp = methodWithArgument(nodep, true, false, nodep->findLogicBoolDType(), + 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 if (nodep->name() == "find_index" || nodep->name() == "find_first_index" + || nodep->name() == "find_last_index") { + AstWith* withp = methodWithArgument(nodep, true, false, nodep->findLogicBoolDType(), + adtypep->subDTypep()); + methodOkArguments(nodep, 0, 0); + methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); + newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), + nodep->name(), withp); + newp->dtypep(newp->findQueueIndexDType()); + if (!nodep->firstAbovep()) newp->makeStatement(); } else { nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in queue 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); @@ -2884,6 +3029,18 @@ private: nodep->v3error("Unknown built-in string method " << nodep->prettyNameQ()); } } + AstQueueDType* queueDTypeIndexedBy(AstNodeDType* indexDTypep) { + // Return a Queue data type with the specified index, remembering so can use again if + // needed + if (AstQueueDType* queuep = m_queueDTypeIndexed[indexDTypep]) { + return queuep; + } else { + auto* newp = new AstQueueDType(indexDTypep->fileline(), indexDTypep, nullptr); + v3Global.rootp()->typeTablep()->addTypesp(newp); + m_queueDTypeIndexed[indexDTypep] = newp; + return newp; + } + } virtual void visit(AstNew* nodep) override { if (nodep->didWidthAndSet()) return; @@ -4179,7 +4336,24 @@ private: } virtual void visit(AstWith* nodep) override { // Should otherwise be underneath a method call - nodep->v3warn(E_UNSUPPORTED, "Unsupported: with statements in this context"); + AstNodeDType* vdtypep = m_vup->dtypeNullSkipRefp(); + VL_RESTORER(m_lambdaArgRefp); + { + m_lambdaArgRefp = nodep->argrefp(); + userIterateChildren(nodep->argrefp(), nullptr); + if (vdtypep) { + userIterateAndNext(nodep->exprp(), WidthVP(nodep->dtypep(), PRELIM).p()); + } else { // 'sort with' allows arbitrary type + userIterateAndNext(nodep->exprp(), WidthVP(SELF, PRELIM).p()); + } + nodep->dtypeFrom(nodep->exprp()); + iterateCheckAssign(nodep, "'with' return value", nodep->exprp(), FINAL, + nodep->dtypep()); + } + } + virtual void visit(AstLambdaArgRef* nodep) override { + UASSERT_OBJ(m_lambdaArgRefp, nodep, "LambdaArgRef not underneath with lambda"); + nodep->dtypeFrom(m_lambdaArgRefp); } virtual void visit(AstNetlist* nodep) override { // Iterate modules backwards, in bottom-up order. That's faster diff --git a/test_regress/t/t_array_method.out b/test_regress/t/t_array_method.out index 85bbc2913..48e2a0a0f 100644 --- a/test_regress/t/t_array_method.out +++ b/test_regress/t/t_array_method.out @@ -26,10 +26,6 @@ : ... In instance t 38 | qi = q.unique_index; qi.sort; | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_array_method.v:38:31: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 38 | qi = q.unique_index; qi.sort; - | ^~~~ %Error: t/t_array_method.v:40:9: Unknown built-in array method 'reverse' : ... In instance t 40 | q.reverse; @@ -70,10 +66,6 @@ : ... In instance t 61 | qi = q.find_index with (item == 2); qi.sort; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_array_method.v:61:46: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 61 | qi = q.find_index with (item == 2); qi.sort; - | ^~~~ %Error: t/t_array_method.v:63:14: Unknown built-in array method 'find_first_index' : ... In instance t 63 | qi = q.find_first_index with (item == 2); @@ -86,10 +78,6 @@ : ... In instance t 68 | qi = q.find_index with (item == 20); qi.sort; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_array_method.v:68:47: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 68 | qi = q.find_index with (item == 20); qi.sort; - | ^~~~ %Error: t/t_array_method.v:70:14: Unknown built-in array method 'find_first_index' : ... In instance t 70 | qi = q.find_first_index with (item == 20); diff --git a/test_regress/t/t_assoc_method.out b/test_regress/t/t_assoc_method.out deleted file mode 100644 index 0f16007ef..000000000 --- a/test_regress/t/t_assoc_method.out +++ /dev/null @@ -1,153 +0,0 @@ -%Error: t/t_assoc_method.v:31:14: Unknown built-in associative array method 'unique' - : ... In instance t - 31 | qv = q.unique; - | ^~~~~~ -%Error: t/t_assoc_method.v:33:15: Unknown built-in associative array method 'unique' - : ... In instance t - 33 | qv = qe.unique; - | ^~~~~~ -%Error: t/t_assoc_method.v:35:14: Unknown built-in associative array method 'unique_index' - : ... In instance t - 35 | qi = q.unique_index; qi.sort; - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assoc_method.v:35:31: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 35 | qi = q.unique_index; qi.sort; - | ^~~~ -%Error: t/t_assoc_method.v:37:15: Unknown built-in associative array method 'unique_index' - : ... In instance t - 37 | qv = qe.unique_index; - | ^~~~~~~~~~~~ -%Error: t/t_assoc_method.v:42:14: Unknown built-in associative array method 'find' - : ... In instance t - 42 | qv = q.find with (item == 2); - | ^~~~ -%Error: t/t_assoc_method.v:44:14: Unknown built-in associative array method 'find_first' - : ... In instance t - 44 | qv = q.find_first with (item == 2); - | ^~~~~~~~~~ -%Error: t/t_assoc_method.v:46:14: Unknown built-in associative array method 'find_last' - : ... In instance t - 46 | qv = q.find_last with (item == 2); - | ^~~~~~~~~ -%Error: t/t_assoc_method.v:49:14: Unknown built-in associative array method 'find' - : ... In instance t - 49 | qv = q.find with (item == 20); - | ^~~~ -%Error: t/t_assoc_method.v:51:14: Unknown built-in associative array method 'find_first' - : ... In instance t - 51 | qv = q.find_first with (item == 20); - | ^~~~~~~~~~ -%Error: t/t_assoc_method.v:53:14: Unknown built-in associative array method 'find_last' - : ... In instance t - 53 | qv = q.find_last with (item == 20); - | ^~~~~~~~~ -%Error: t/t_assoc_method.v:56:14: Unknown built-in associative array method 'find_index' - : ... In instance t - 56 | qi = q.find_index with (item == 2); qi.sort; - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assoc_method.v:56:46: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 56 | qi = q.find_index with (item == 2); qi.sort; - | ^~~~ -%Error: t/t_assoc_method.v:58:14: Unknown built-in associative array method 'find_first_index' - : ... In instance t - 58 | qi = q.find_first_index with (item == 2); - | ^~~~~~~~~~~~~~~~ -%Error: t/t_assoc_method.v:60:14: Unknown built-in associative array method 'find_last_index' - : ... In instance t - 60 | qi = q.find_last_index with (item == 2); - | ^~~~~~~~~~~~~~~ -%Error: t/t_assoc_method.v:63:14: Unknown built-in associative array method 'find_index' - : ... In instance t - 63 | qi = q.find_index with (item == 20); qi.sort; - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assoc_method.v:63:47: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 63 | qi = q.find_index with (item == 20); qi.sort; - | ^~~~ -%Error: t/t_assoc_method.v:65:14: Unknown built-in associative array method 'find_first_index' - : ... In instance t - 65 | qi = q.find_first_index with (item == 20); - | ^~~~~~~~~~~~~~~~ -%Error: t/t_assoc_method.v:67:14: Unknown built-in associative array method 'find_last_index' - : ... In instance t - 67 | qi = q.find_last_index with (item == 20); - | ^~~~~~~~~~~~~~~ -%Error: t/t_assoc_method.v:70:14: Unknown built-in associative array method 'min' - : ... In instance t - 70 | qv = q.min; - | ^~~ -%Error: t/t_assoc_method.v:72:14: Unknown built-in associative array method 'max' - : ... In instance t - 72 | qv = q.max; - | ^~~ -%Error: t/t_assoc_method.v:75:15: Unknown built-in associative array method 'min' - : ... In instance t - 75 | qv = qe.min; - | ^~~ -%Error: t/t_assoc_method.v:77:15: Unknown built-in associative array method 'max' - : ... In instance t - 77 | qv = qe.max; - | ^~~ -%Error: t/t_assoc_method.v:82:13: Unknown built-in associative array method 'sum' - : ... In instance t - 82 | i = q.sum; do if ((i) !== (32'hc)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",82, (i), (32'hc)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:83:13: Unknown built-in associative array method 'sum' - : ... In instance t - 83 | i = q.sum with (item + 1); do if ((i) !== (32'h11)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",83, (i), (32'h11)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:84:13: Unknown built-in associative array method 'product' - : ... In instance t - 84 | i = q.product; do if ((i) !== (32'h30)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",84, (i), (32'h30)); $stop; end while(0);; - | ^~~~~~~ -%Error: t/t_assoc_method.v:85:13: Unknown built-in associative array method 'product' - : ... In instance t - 85 | i = q.product with (item + 1); do if ((i) !== (32'h168)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",85, (i), (32'h168)); $stop; end while(0);; - | ^~~~~~~ -%Error: t/t_assoc_method.v:87:14: Unknown built-in associative array method 'sum' - : ... In instance t - 87 | i = qe.sum; do if ((i) !== (32'h0)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",87, (i), (32'h0)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:88:14: Unknown built-in associative array method 'product' - : ... In instance t - 88 | i = qe.product; do if ((i) !== (32'h0)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",88, (i), (32'h0)); $stop; end while(0);; - | ^~~~~~~ -%Error: t/t_assoc_method.v:91:13: Unknown built-in associative array method 'and' - : ... In instance t - 91 | i = q.and; do if ((i) !== (32'b1000)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",91, (i), (32'b1000)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:92:13: Unknown built-in associative array method 'and' - : ... In instance t - 92 | i = q.and with (item + 1); do if ((i) !== (32'b1001)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",92, (i), (32'b1001)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:93:13: Unknown built-in associative array method 'or' - : ... In instance t - 93 | i = q.or; do if ((i) !== (32'b1110)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",93, (i), (32'b1110)); $stop; end while(0);; - | ^~ -%Error: t/t_assoc_method.v:94:13: Unknown built-in associative array method 'or' - : ... In instance t - 94 | i = q.or with (item + 1); do if ((i) !== (32'b1111)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",94, (i), (32'b1111)); $stop; end while(0);; - | ^~ -%Error: t/t_assoc_method.v:95:13: Unknown built-in associative array method 'xor' - : ... In instance t - 95 | i = q.xor; do if ((i) !== (32'b0110)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",95, (i), (32'b0110)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:96:13: Unknown built-in associative array method 'xor' - : ... In instance t - 96 | i = q.xor with (item + 1); do if ((i) !== (32'b0110)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",96, (i), (32'b0110)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:98:14: Unknown built-in associative array method 'and' - : ... In instance t - 98 | i = qe.and; do if ((i) !== (32'b0)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",98, (i), (32'b0)); $stop; end while(0);; - | ^~~ -%Error: t/t_assoc_method.v:99:14: Unknown built-in associative array method 'or' - : ... In instance t - 99 | i = qe.or; do if ((i) !== (32'b0)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",99, (i), (32'b0)); $stop; end while(0);; - | ^~ -%Error: t/t_assoc_method.v:100:14: Unknown built-in associative array method 'xor' - : ... In instance t - 100 | i = qe.xor; do if ((i) !== (32'b0)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", "t/t_assoc_method.v",100, (i), (32'b0)); $stop; end while(0);; - | ^~~ -%Error: Exiting due to diff --git a/test_regress/t/t_assoc_method.pl b/test_regress/t/t_assoc_method.pl index be66c40e6..b46d46042 100755 --- a/test_regress/t/t_assoc_method.pl +++ b/test_regress/t/t_assoc_method.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_assoc_method.v b/test_regress/t/t_assoc_method.v index 40e05ca2f..29c8372e1 100644 --- a/test_regress/t/t_assoc_method.v +++ b/test_regress/t/t_assoc_method.v @@ -18,7 +18,7 @@ module t (/*AUTOARG*/); string v; q = '{10:1, 11:2, 12:2, 13:4, 14:3}; - v = $sformatf("%p", q); `checks(v, "'{0xa:1, 0xb:2, 0xc:2, 0xd:4, 0xe:3} "); + v = $sformatf("%p", q); `checks(v, "'{'ha:'h1, 'hb:'h2, 'hc:'h2, 'hd:'h4, 'he:'h3} "); // NOT tested: with ... selectors @@ -29,22 +29,22 @@ module t (/*AUTOARG*/); v = $sformatf("%p", qe); `checks(v, "'{}"); qv = q.unique; - v = $sformatf("%p", qv); `checks(v, "'{1, 2, 4, 3} "); + v = $sformatf("%p", qv); `checks(v, "'{'h1, 'h2, 'h4, 'h3} "); qv = qe.unique; v = $sformatf("%p", qv); `checks(v, "'{}"); qi = q.unique_index; qi.sort; - v = $sformatf("%p", qi); `checks(v, "'{10, 11, 13, 14} "); - qv = qe.unique_index; - v = $sformatf("%p", qv); `checks(v, "'{}"); + v = $sformatf("%p", qi); `checks(v, "'{'ha, 'hb, 'hd, 'he} "); + qi = qe.unique_index; + v = $sformatf("%p", qi); `checks(v, "'{}"); // These require an with clause or are illegal // TODO add a lint check that with clause is provided qv = q.find with (item == 2); - v = $sformatf("%p", qv); `checks(v, "'{2, 2} "); + v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h2} "); qv = q.find_first with (item == 2); - v = $sformatf("%p", qv); `checks(v, "'{2} "); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); qv = q.find_last with (item == 2); - v = $sformatf("%p", qv); `checks(v, "'{2} "); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); qv = q.find with (item == 20); v = $sformatf("%p", qv); `checks(v, "'{}"); @@ -54,11 +54,11 @@ module t (/*AUTOARG*/); v = $sformatf("%p", qv); `checks(v, "'{}"); qi = q.find_index with (item == 2); qi.sort; - v = $sformatf("%p", qi); `checks(v, "'{11, 12} "); + v = $sformatf("%p", qi); `checks(v, "'{'hb, 'hc} "); qi = q.find_first_index with (item == 2); - v = $sformatf("%p", qi); `checks(v, "'{11} "); + v = $sformatf("%p", qi); `checks(v, "'{'hb} "); qi = q.find_last_index with (item == 2); - v = $sformatf("%p", qi); `checks(v, "'{12} "); + v = $sformatf("%p", qi); `checks(v, "'{'hc} "); qi = q.find_index with (item == 20); qi.sort; v = $sformatf("%p", qi); `checks(v, "'{}"); @@ -68,9 +68,9 @@ module t (/*AUTOARG*/); v = $sformatf("%p", qi); `checks(v, "'{}"); qv = q.min; - v = $sformatf("%p", qv); `checks(v, "'{1} "); + v = $sformatf("%p", qv); `checks(v, "'{'h1} "); qv = q.max; - v = $sformatf("%p", qv); `checks(v, "'{4} "); + v = $sformatf("%p", qv); `checks(v, "'{'h4} "); qv = qe.min; v = $sformatf("%p", qv); `checks(v, "'{}"); @@ -79,25 +79,40 @@ module t (/*AUTOARG*/); // 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 = 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); + 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 = 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 = 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; diff --git a/test_regress/t/t_dynarray_method.pl b/test_regress/t/t_dynarray_method.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_dynarray_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_dynarray_method.v b/test_regress/t/t_dynarray_method.v new file mode 100644 index 000000000..21a778dc5 --- /dev/null +++ b/test_regress/t/t_dynarray_method.v @@ -0,0 +1,168 @@ +// 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); + +`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t (/*AUTOARG*/); + + string s[] = { "hello", "sad", "sad", "world" }; + + initial begin + int d[]; + int de[]; // Empty + int qv[$]; // Value returns + int qvunused[$]; // Value returns (unused) + int qi[$]; // Index returns + int i; + string v; + + d = '{1, 2, 2, 4, 3}; + v = $sformatf("%p", d); `checks(v, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); + d = {1, 2, 2, 4, 3}; + v = $sformatf("%p", d); `checks(v, "'{'h1, 'h2, 'h2, 'h4, 'h3} "); + + // sort/rsort with clause is the field to use for the sorting + d.sort; + v = $sformatf("%p", d); `checks(v, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + d.sort with (10 - item); + v = $sformatf("%p", d); `checks(v, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + d.sort(x) with (10 - x); + v = $sformatf("%p", d); `checks(v, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + de.sort(x) with (10 - x); + v = $sformatf("%p", de); `checks(v, "'{}"); + d.rsort; + v = $sformatf("%p", d); `checks(v, "'{'h4, 'h3, 'h2, 'h2, 'h1} "); + d.rsort with (10 - item); + v = $sformatf("%p", d); `checks(v, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + de.rsort(x) with (10 - x); + v = $sformatf("%p", d); `checks(v, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + + d = '{2, 2, 4, 1, 3}; + qv = d.unique; + v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h4, 'h1, 'h3} "); + qv = de.unique; + `checkh(qv.size(), 0); + qi = d.unique_index; qv.sort; + v = $sformatf("%p", qi); `checks(v, "'{'h0, 'h2, 'h3, 'h4} "); + qi = de.unique_index; + `checkh(qi.size(), 0); + + d.reverse; + v = $sformatf("%p", d); `checks(v, "'{'h3, 'h1, 'h4, 'h2, 'h2} "); + de.reverse; + `checkh(de.size(), 0); + d.shuffle(); d.sort; + v = $sformatf("%p", d); `checks(v, "'{'h1, 'h2, 'h2, 'h3, 'h4} "); + de.shuffle(); + `checkh(de.size(), 0); + + // These require an with clause or are illegal + // TODO add a lint check that with clause is provided + qv = d.find with (item == 2); + v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h2} "); + qv = d.find_first with (item == 2); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); + qv = d.find_last with (item == 2); + v = $sformatf("%p", qv); `checks(v, "'{'h2} "); + + qv = d.find with (item == 20); + `checkh(qv.size, 0); + qv = d.find_first with (item == 20); + `checkh(qv.size, 0); + qv = d.find_last with (item == 20); + `checkh(qv.size, 0); + + // Check gate eater with Lambda variable removal + qvunused = d.find with (item == 20); + + qi = d.find_index with (item == 2); + qi.sort; v = $sformatf("%p", qi); `checks(v, "'{'h1, 'h2} "); + qi = d.find_first_index with (item == 2); + v = $sformatf("%p", qi); `checks(v, "'{'h1} "); + qi = d.find_last_index with (item == 2); + v = $sformatf("%p", qi); `checks(v, "'{'h2} "); + + i = 2; + qi = d.find_index with (item == i); + qi.sort; v = $sformatf("%p", qi); `checks(v, "'{'h1, 'h2} "); + + qi = d.find_index with (item == 20); qi.sort; + `checkh(qi.size, 0); + qi = d.find_first_index with (item == 20); + `checkh(qi.size, 0); + qi = d.find_last_index with (item == 20); + `checkh(qi.size, 0); + + qv = d.min; + v = $sformatf("%p", qv); `checks(v, "'{'h1} "); + qv = d.max; + v = $sformatf("%p", qv); `checks(v, "'{'h4} "); + qv = de.min; + v = $sformatf("%p", qv); `checks(v, "'{}"); + qv = de.max; + v = $sformatf("%p", qv); `checks(v, "'{}"); + + // Reduction methods + i = d.sum; + `checkh(i, 32'hc); + i = d.sum with (item + 1); + `checkh(i, 32'h11); + i = d.sum(myi) with (myi + 1); + `checkh(i, 32'h11); + i = d.sum with (1); // unused 'index' + `checkh(i, 32'h5); + i = d.sum(unused) with (1); // unused 'unused' + `checkh(i, 32'h5); + + i = d.product; + `checkh(i, 32'h30); + i = d.product with (item + 1); + `checkh(i, 32'h168); + + i = de.sum; + `checkh(i, 32'h0); + + i = de.product; + `checkh(i, 32'h0); + + d = '{32'b1100, 32'b1010}; + + i = d.and; + `checkh(i, 32'b1000); + i = d.and with (item + 1); + `checkh(i, 32'b1001); + i = d.or; + `checkh(i, 32'b1110); + i = d.or with (item + 1); + `checkh(i, 32'b1111); + i = d.xor; + `checkh(i, 32'b0110); + i = d.xor with (item + 1); + `checkh(i, 32'b0110); + + i = de.and; + `checkh(i, 32'b0); + i = de.or; + `checkh(i, 32'b0); + i = de.xor; + `checkh(i, 32'b0); + + `checks(s[1], "sad"); + qi = s.find_first_index with (item == "sad"); + `checkh(qi.size, 1); + `checkh(qi[0], 1); + qi = s.find_last_index with (item == "sad"); + `checkh(qi.size, 1); + `checkh(qi[0], 2); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_queue_method.out b/test_regress/t/t_queue_method.out deleted file mode 100644 index 57e6f4f83..000000000 --- a/test_regress/t/t_queue_method.out +++ /dev/null @@ -1,201 +0,0 @@ -%Error-UNSUPPORTED: t/t_queue_method.v:26:9: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 26 | q.sort; - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:28:9: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 28 | q.sort with (10 - item); - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:30:9: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 30 | q.sort(x) with (10 - x); - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:32:10: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 32 | qe.sort(x) with (10 - x); - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:34:9: Unsupported/unknown built-in queue method 'rsort' - : ... In instance t - 34 | q.rsort; - | ^~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:36:9: Unsupported/unknown built-in queue method 'rsort' - : ... In instance t - 36 | q.rsort with (10 - item); - | ^~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:38:10: Unsupported/unknown built-in queue method 'rsort' - : ... In instance t - 38 | qe.rsort(x) with (10 - x); - | ^~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:42:14: Unsupported/unknown built-in queue method 'unique' - : ... In instance t - 42 | qv = q.unique; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:44:15: Unsupported/unknown built-in queue method 'unique' - : ... In instance t - 44 | qv = qe.unique; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:46:14: Unsupported/unknown built-in queue method 'unique_index' - : ... In instance t - 46 | qi = q.unique_index; qv.sort; - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:46:31: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 46 | qi = q.unique_index; qv.sort; - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:48:15: Unsupported/unknown built-in queue method 'unique_index' - : ... In instance t - 48 | qi = qe.unique_index; - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:51:9: Unsupported/unknown built-in queue method 'reverse' - : ... In instance t - 51 | q.reverse; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:53:10: Unsupported/unknown built-in queue method 'reverse' - : ... In instance t - 53 | qe.reverse; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:55:9: Unsupported/unknown built-in queue method 'shuffle' - : ... In instance t - 55 | q.shuffle(); q.sort; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:55:22: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 55 | q.shuffle(); q.sort; - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:57:10: Unsupported/unknown built-in queue method 'shuffle' - : ... In instance t - 57 | qe.shuffle(); - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:62:14: Unsupported/unknown built-in queue method 'find' - : ... In instance t - 62 | qv = q.find with (item == 2); - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:64:14: Unsupported/unknown built-in queue method 'find_first' - : ... In instance t - 64 | qv = q.find_first with (item == 2); - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:66:14: Unsupported/unknown built-in queue method 'find_last' - : ... In instance t - 66 | qv = q.find_last with (item == 2); - | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:69:14: Unsupported/unknown built-in queue method 'find' - : ... In instance t - 69 | qv = q.find with (item == 20); - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:71:14: Unsupported/unknown built-in queue method 'find_first' - : ... In instance t - 71 | qv = q.find_first with (item == 20); - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:73:14: Unsupported/unknown built-in queue method 'find_last' - : ... In instance t - 73 | qv = q.find_last with (item == 20); - | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:76:14: Unsupported/unknown built-in queue method 'find_index' - : ... In instance t - 76 | qi = q.find_index with (item == 2); - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:77:10: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 77 | qi.sort; v = $sformatf("%p", qi); do if ((v) !== ("'{'h1, 'h2} ")) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", "t/t_queue_method.v",77, (v), ("'{'h1, 'h2} ")); $stop; end while(0);; - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:78:14: Unsupported/unknown built-in queue method 'find_first_index' - : ... In instance t - 78 | qi = q.find_first_index with (item == 2); - | ^~~~~~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:80:14: Unsupported/unknown built-in queue method 'find_last_index' - : ... In instance t - 80 | qi = q.find_last_index with (item == 2); - | ^~~~~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:83:14: Unsupported/unknown built-in queue method 'find_index' - : ... In instance t - 83 | qi = q.find_index with (item == 20); qi.sort; - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:83:47: Unsupported/unknown built-in queue method 'sort' - : ... In instance t - 83 | qi = q.find_index with (item == 20); qi.sort; - | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:85:14: Unsupported/unknown built-in queue method 'find_first_index' - : ... In instance t - 85 | qi = q.find_first_index with (item == 20); - | ^~~~~~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:87:14: Unsupported/unknown built-in queue method 'find_last_index' - : ... In instance t - 87 | qi = q.find_last_index with (item == 20); - | ^~~~~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:90:14: Unsupported/unknown built-in queue method 'min' - : ... In instance t - 90 | qv = q.min; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:92:14: Unsupported/unknown built-in queue method 'max' - : ... In instance t - 92 | qv = q.max; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:94:15: Unsupported/unknown built-in queue method 'min' - : ... In instance t - 94 | qv = qe.min; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:96:15: Unsupported/unknown built-in queue method 'max' - : ... In instance t - 96 | qv = qe.max; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:100:13: Unsupported/unknown built-in queue method 'sum' - : ... In instance t - 100 | i = q.sum; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:102:13: Unsupported/unknown built-in queue method 'sum' - : ... In instance t - 102 | i = q.sum with (item + 1); - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:104:13: Unsupported/unknown built-in queue method 'product' - : ... In instance t - 104 | i = q.product; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:106:13: Unsupported/unknown built-in queue method 'product' - : ... In instance t - 106 | i = q.product with (item + 1); - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:109:14: Unsupported/unknown built-in queue method 'sum' - : ... In instance t - 109 | i = qe.sum; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:111:14: Unsupported/unknown built-in queue method 'product' - : ... In instance t - 111 | i = qe.product; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method.v:115:13: Unsupported/unknown built-in queue method 'and' - : ... In instance t - 115 | i = q.and; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:117:13: Unsupported/unknown built-in queue method 'and' - : ... In instance t - 117 | i = q.and with (item + 1); - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:119:13: Unsupported/unknown built-in queue method 'or' - : ... In instance t - 119 | i = q.or; - | ^~ -%Error-UNSUPPORTED: t/t_queue_method.v:121:13: Unsupported/unknown built-in queue method 'or' - : ... In instance t - 121 | i = q.or with (item + 1); - | ^~ -%Error-UNSUPPORTED: t/t_queue_method.v:123:13: Unsupported/unknown built-in queue method 'xor' - : ... In instance t - 123 | i = q.xor; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:125:13: Unsupported/unknown built-in queue method 'xor' - : ... In instance t - 125 | i = q.xor with (item + 1); - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:128:14: Unsupported/unknown built-in queue method 'and' - : ... In instance t - 128 | i = qe.and; - | ^~~ -%Error-UNSUPPORTED: t/t_queue_method.v:130:14: Unsupported/unknown built-in queue method 'or' - : ... In instance t - 130 | i = qe.or; - | ^~ -%Error-UNSUPPORTED: t/t_queue_method.v:132:14: Unsupported/unknown built-in queue method 'xor' - : ... In instance t - 132 | i = qe.xor; - | ^~~ -%Error: Exiting due to diff --git a/test_regress/t/t_queue_method.pl b/test_regress/t/t_queue_method.pl index be66c40e6..b46d46042 100755 --- a/test_regress/t/t_queue_method.pl +++ b/test_regress/t/t_queue_method.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_queue_method.v b/test_regress/t/t_queue_method.v index 5e0e478fb..d5429c88d 100644 --- a/test_regress/t/t_queue_method.v +++ b/test_regress/t/t_queue_method.v @@ -15,6 +15,7 @@ module t (/*AUTOARG*/); int q[$]; int qe[$]; // Empty int qv[$]; // Value returns + int qvunused[$]; // Value returns (unused) int qi[$]; // Index returns int i; string v; @@ -73,6 +74,9 @@ module t (/*AUTOARG*/); qv = q.find_last with (item == 20); `checkh(qv.size, 0); + // Check gate eater with Lambda variable removal + qvunused = q.find with (item == 20); + qi = q.find_index with (item == 2); qi.sort; v = $sformatf("%p", qi); `checks(v, "'{'h1, 'h2} "); qi = q.find_first_index with (item == 2); @@ -80,6 +84,10 @@ module t (/*AUTOARG*/); qi = q.find_last_index with (item == 2); v = $sformatf("%p", qi); `checks(v, "'{'h2} "); + i = 2; + qi = q.find_index with (item == i); + qi.sort; v = $sformatf("%p", qi); `checks(v, "'{'h1, 'h2} "); + qi = q.find_index with (item == 20); qi.sort; `checkh(qi.size, 0); qi = q.find_first_index with (item == 20); @@ -101,6 +109,13 @@ module t (/*AUTOARG*/); `checkh(i, 32'hc); i = q.sum with (item + 1); `checkh(i, 32'h11); + i = q.sum(myi) with (myi + 1); + `checkh(i, 32'h11); + i = q.sum with (1); // unused 'index' + `checkh(i, 32'h5); + i = q.sum(unused) with (1); // unused 'unused' + `checkh(i, 32'h5); + i = q.product; `checkh(i, 32'h30); i = q.product with (item + 1); @@ -108,6 +123,7 @@ module t (/*AUTOARG*/); i = qe.sum; `checkh(i, 32'h0); + i = qe.product; `checkh(i, 32'h0); diff --git a/test_regress/t/t_queue_method_bad.out b/test_regress/t/t_queue_method_bad.out index 429388dca..584d57b0f 100755 --- a/test_regress/t/t_queue_method_bad.out +++ b/test_regress/t/t_queue_method_bad.out @@ -1,37 +1,37 @@ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:15:14: Unsupported/unknown built-in queue method 'unique' - : ... In instance t +%Error: t/t_queue_method_bad.v:15:21: 'with' not legal on this method + : ... In instance t 15 | qv = q.unique with (1); - | ^~~~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:16:9: Unsupported/unknown built-in queue method 'reverse' - : ... In instance t + | ^~~~ +%Error: t/t_queue_method_bad.v:16:9: The 1 arguments passed to .reverse method does not match its requiring 0 arguments + : ... In instance t 16 | q.reverse(1); | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:17:9: Unsupported/unknown built-in queue method 'shuffle' - : ... In instance t +%Error: t/t_queue_method_bad.v:17:9: The 1 arguments passed to .shuffle method does not match its requiring 0 arguments + : ... In instance t 17 | q.shuffle(1); | ^~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:18:14: Unsupported/unknown built-in queue method 'find' - : ... In instance t +%Error: t/t_queue_method_bad.v:18:14: 'with' statement is required for .find method + : ... In instance t 18 | qv = q.find; | ^~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:19:14: Unsupported/unknown built-in queue method 'find_first' - : ... In instance t +%Error: t/t_queue_method_bad.v:19:14: 'with' statement is required for .find_first method + : ... In instance t 19 | qv = q.find_first; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:20:14: Unsupported/unknown built-in queue method 'find_last' - : ... In instance t +%Error: t/t_queue_method_bad.v:20:14: 'with' statement is required for .find_last method + : ... In instance t 20 | qv = q.find_last; | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:21:14: Unsupported/unknown built-in queue method 'find_index' - : ... In instance t +%Error: t/t_queue_method_bad.v:21:14: 'with' statement is required for .find_index method + : ... In instance t 21 | qi = q.find_index; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:22:14: Unsupported/unknown built-in queue method 'find_first_index' - : ... In instance t +%Error: t/t_queue_method_bad.v:22:14: 'with' statement is required for .find_first_index method + : ... In instance t 22 | qi = q.find_first_index; | ^~~~~~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_queue_method_bad.v:23:14: Unsupported/unknown built-in queue method 'find_last_index' - : ... In instance t +%Error: t/t_queue_method_bad.v:23:14: 'with' statement is required for .find_last_index method + : ... In instance t 23 | qi = q.find_last_index; | ^~~~~~~~~~~~~~~ %Error: t/t_queue_method_bad.v:25:19: 'with' not legal on this method diff --git a/test_regress/t/t_with.out b/test_regress/t/t_with.out deleted file mode 100644 index 4b4dae672..000000000 --- a/test_regress/t/t_with.out +++ /dev/null @@ -1,41 +0,0 @@ -%Error-UNSUPPORTED: t/t_with.v:19:23: Unsupported/unknown built-in queue method 'find' - : ... In instance t - 19 | found = aliases.find(i) with (i == tofind); - | ^~~~ -%Error-UNSUPPORTED: t/t_with.v:21:15: Unsupported/unknown built-in queue method 'find' - : ... In instance t - 21 | aliases.find(i) with (i == tofind); - | ^~~~ -%Error-UNSUPPORTED: t/t_with.v:24:23: Unsupported/unknown built-in queue method 'find' - : ... In instance t - 24 | found = aliases.find with (item == i); - | ^~~~ -%Error-UNSUPPORTED: t/t_with.v:25:15: Unsupported/unknown built-in queue method 'find' - : ... In instance t - 25 | aliases.find with (item == i); - | ^~~~ -%Error-UNSUPPORTED: t/t_with.v:29:23: Unsupported/unknown built-in queue method 'unique' - : ... In instance t - 29 | found = aliases.unique with (id); - | ^~~~~~ -%Error-UNSUPPORTED: t/t_with.v:30:23: Unsupported/unknown built-in queue method 'unique' - : ... In instance t - 30 | found = aliases.unique() with (id); - | ^~~~~~ -%Error-UNSUPPORTED: t/t_with.v:31:23: Unsupported/unknown built-in queue method 'unique' - : ... In instance t - 31 | found = aliases.unique(i) with (id); - | ^~~~~~ -%Error-UNSUPPORTED: t/t_with.v:32:19: Unsupported/unknown built-in queue method 'or' - : ... In instance t - 32 | i = aliases.or(v) with (v); - | ^~ -%Error-UNSUPPORTED: t/t_with.v:33:19: Unsupported/unknown built-in queue method 'and' - : ... In instance t - 33 | i = aliases.and(v) with (v); - | ^~~ -%Error-UNSUPPORTED: t/t_with.v:34:19: Unsupported/unknown built-in queue method 'xor' - : ... In instance t - 34 | i = aliases.xor(v) with (v); - | ^~~ -%Error: Exiting due to diff --git a/test_regress/t/t_with.pl b/test_regress/t/t_with.pl index c6e10f70b..c81275cdf 100755 --- a/test_regress/t/t_with.pl +++ b/test_regress/t/t_with.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_with.v b/test_regress/t/t_with.v index c0989a818..015cdc295 100644 --- a/test_regress/t/t_with.v +++ b/test_regress/t/t_with.v @@ -14,9 +14,11 @@ module t (/*AUTOARG*/); int found[$]; int id; int i; + aliases = '{ 1, 4, 6, 8}; tofind = 6; - found = aliases.find(i) with (i == tofind); + found = aliases.find with (item == 1); + found = aliases.find(j) with (j == tofind); // And as function aliases.find(i) with (i == tofind); @@ -26,9 +28,9 @@ module t (/*AUTOARG*/); // Unique (array method) id = 4; - found = aliases.unique with (id); - found = aliases.unique() with (id); - found = aliases.unique(i) with (id); + found = aliases.find with (id); + found = aliases.find() with (item == id); + found = aliases.find(i) with (i == id); i = aliases.or(v) with (v); i = aliases.and(v) with (v); i = aliases.xor(v) with (v); diff --git a/test_regress/t/t_with_suggest_bad.out b/test_regress/t/t_with_suggest_bad.out new file mode 100644 index 000000000..2ad8ff9de --- /dev/null +++ b/test_regress/t/t_with_suggest_bad.out @@ -0,0 +1,9 @@ +%Error: t/t_with_suggest_bad.v:16:25: Can't find definition of variable: 'itemm' + : ... Suggested alternative: 'item' + 16 | qv = q.find with (itemm == 2); + | ^~~~~ +%Error: t/t_with_suggest_bad.v:18:37: Can't find definition of variable: 'misspelledd' + : ... Suggested alternative: 'misspelled' + 18 | qv = q.find(misspelled) with (misspelledd == 2); + | ^~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_with_suggest_bad.pl b/test_regress/t/t_with_suggest_bad.pl new file mode 100755 index 000000000..85114ac5d --- /dev/null +++ b/test_regress/t/t_with_suggest_bad.pl @@ -0,0 +1,20 @@ +#!/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(vlt => 1); + +lint( + verilator_flags2 => ["--lint-only --language 1800-2017"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_with_suggest_bad.v b/test_regress/t/t_with_suggest_bad.v new file mode 100644 index 000000000..144f18dbb --- /dev/null +++ b/test_regress/t/t_with_suggest_bad.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module for SystemVerilog 'alias' +// +// Simple bi-directional alias test. +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + initial begin + int q[$]; + int qv[$]; // Value returns + q = '{1, 2, 2, 4, 3}; + + qv = q.find with (itemm == 2); + + qv = q.find(misspelled) with (misspelledd == 2); + end + +endmodule