From 95d127226996c23fce58e8d30df8cafe0ab45a31 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 25 Oct 2020 21:05:22 -0400 Subject: [PATCH] Support associative array pattern assignments and defaults. --- Changes | 2 + include/verilated_heavy.h | 10 ++++ src/V3AstNodes.h | 43 ++++++++++++++++++ src/V3EmitC.cpp | 18 ++++++++ src/V3Width.cpp | 58 +++++++++++++++++++++--- test_regress/t/t_assoc.v | 5 +- test_regress/t/t_assoc_pattern_unsup.out | 5 -- test_regress/t/t_assoc_pattern_unsup.pl | 19 -------- test_regress/t/t_assoc_pattern_unsup.v | 28 ------------ 9 files changed, 127 insertions(+), 61 deletions(-) delete mode 100644 test_regress/t/t_assoc_pattern_unsup.out delete mode 100755 test_regress/t/t_assoc_pattern_unsup.pl delete mode 100644 test_regress/t/t_assoc_pattern_unsup.v diff --git a/Changes b/Changes index c2fff4499..3dc21bc38 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks! **** Support queue slicing (#2326). +**** Support associative array pattern assignments and defaults. + **** Fix WIDTH warnings on comparisons with nullptr (#2602). [Rupert Swarbrick] diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index e8d9dbde3..2d110567a 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -196,6 +196,16 @@ public: return it->second; } } + // Setting as a chained operation + VlAssocArray& set(const T_Key& index, const T_Value& value) { + at(index) = value; + return *this; + } + VlAssocArray& setDefault(const T_Value& value) { + atDefault() = value; + return *this; + } + // For save/restore const_iterator begin() const { return m_map.begin(); } const_iterator end() const { return m_map.end(); } diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index fdbcc205c..e2e5d48dc 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -4599,6 +4599,49 @@ public: virtual bool same(const AstNode* samep) const override { return true; } }; +class AstConsAssoc : public AstNodeMath { + // Construct an assoc array and return object, '{} + // Parents: math + // Children: expression (elements or other queues) +public: + AstConsAssoc(FileLine* fl, AstNode* defaultp) + : ASTGEN_SUPER(fl) { + setNOp1p(defaultp); + } + ASTNODE_NODE_FUNCS(ConsAssoc) + 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 V3Hash sameHash() const override { return V3Hash(); } + virtual bool same(const AstNode* samep) const override { return true; } +}; +class AstSetAssoc : public AstNodeMath { + // Set an assoc array element and return object, '{} + // Parents: math + // Children: expression (elements or other queues) +public: + AstSetAssoc(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep) + : ASTGEN_SUPER(fl) { + setOp1p(lhsp); + setNOp2p(keyp); + setOp3p(valuep); + } + ASTNODE_NODE_FUNCS(SetAssoc) + 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 V3Hash sameHash() const override { return V3Hash(); } + virtual bool same(const AstNode* samep) const override { return true; } +}; + class AstConsQueue : public AstNodeMath { // Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs} // Parents: math diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index e84182200..b723ec475 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -1575,6 +1575,24 @@ class EmitCImp : EmitCStmts { } } + virtual void visit(AstConsAssoc* nodep) override { + putbs(nodep->dtypep()->cType("", false, false)); + puts("()"); + if (nodep->defaultp()) { + putbs(".setDefault("); + iterateAndNextNull(nodep->defaultp()); + puts(")"); + } + } + virtual void visit(AstSetAssoc* nodep) override { + iterateAndNextNull(nodep->lhsp()); + putbs(".set("); + iterateAndNextNull(nodep->keyp()); + puts(", "); + putbs(""); + iterateAndNextNull(nodep->valuep()); + puts(")"); + } virtual void visit(AstConsQueue* nodep) override { putbs(nodep->dtypep()->cType("", false, false)); if (!nodep->lhsp()) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a71bb0089..469fc3d03 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1960,7 +1960,32 @@ private: } nodep->dtypeFrom(nodep->itemp()); } - virtual void visit(AstConsQueue* nodep) override { // + virtual void visit(AstConsAssoc* nodep) override { + // Type computed when constructed here + auto* vdtypep = VN_CAST(m_vup->dtypep(), AssocArrayDType); + UASSERT_OBJ(vdtypep, nodep, "ConsAssoc requires assoc 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(AstSetAssoc* nodep) override { + // Type computed when constructed here + auto* vdtypep = VN_CAST(m_vup->dtypep(), AssocArrayDType); + UASSERT_OBJ(vdtypep, nodep, "SetsAssoc requires assoc 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->keyDTypep(), + EXTEND_EXP); + iterateCheck(nodep, "value", nodep->valuep(), CONTEXT, FINAL, vdtypep->subDTypep(), + EXTEND_EXP); + } + } + virtual void visit(AstConsQueue* nodep) override { // Type computed when constructed here AstQueueDType* vdtypep = VN_CAST(m_vup->dtypep(), QueueDType); UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type"); @@ -2937,6 +2962,8 @@ private: VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep); } else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) { VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep); + } else if (auto* vdtypep = VN_CAST(dtypep, AssocArrayDType)) { + VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep); } else if (auto* vdtypep = VN_CAST(dtypep, QueueDType)) { VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep); } else if (VN_IS(dtypep, BasicDType) && VN_CAST(dtypep, BasicDType)->isRanged()) { @@ -3044,9 +3071,8 @@ private: } VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } - void patternArray(AstPattern* nodep, AstNodeArrayDType* vdtypep, AstPatMember* defaultp) { - AstNodeArrayDType* arrayp = VN_CAST(vdtypep, NodeArrayDType); - VNumRange range = arrayp->declRange(); + void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) { + VNumRange range = arrayDtp->declRange(); PatVecMap patmap = patVectorMap(nodep, range); UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl); AstNode* newp = nullptr; @@ -3068,11 +3094,11 @@ private: if (patp) { // Don't want the RHS an array - patp->dtypep(arrayp->subDTypep()); + patp->dtypep(arrayDtp->subDTypep()); AstNode* valuep = patternMemberValueIterate(patp); - if (VN_IS(arrayp, UnpackArrayDType)) { + if (VN_IS(arrayDtp, UnpackArrayDType)) { if (!newp) { - AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayp, nullptr); + AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayDtp, nullptr); newp = newap; } VN_CAST(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep); @@ -3099,6 +3125,24 @@ private: // if (debug() >= 9) newp->dumpTree("-apat-out: "); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } + void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) { + AstNode* defaultValuep = nullptr; + if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack(); + AstNode* newp = new AstConsAssoc(nodep->fileline(), defaultValuep); + newp->dtypeFrom(arrayDtp); + for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp; + patp = VN_CAST(patp->nextp(), PatMember)) { + patp->dtypep(arrayDtp->subDTypep()); + AstNode* valuep = patternMemberValueIterate(patp); + AstNode* keyp = patp->keyp(); + auto* newap = new AstSetAssoc(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 patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember* defaultp) { AstNode* newp = new AstConsQueue(nodep->fileline()); newp->dtypeFrom(arrayp); diff --git a/test_regress/t/t_assoc.v b/test_regress/t/t_assoc.v index 8740c3679..6c474516a 100644 --- a/test_regress/t/t_assoc.v +++ b/test_regress/t/t_assoc.v @@ -82,13 +82,14 @@ module t (/*AUTOARG*/ i = a.last(k); `checkh(i, 0); // Patterns & default -`ifndef VERILATOR // Unsupported: Pattern assignment a = '{ "f": "fooed", "b": "bared", default: "defaulted" }; i = a.size(); `checkh(i, 2); // Default doesn't count v = a["f"]; `checks(v, "fooed"); v = a["b"]; `checks(v, "bared"); v = a["NEXISTS"]; `checks(v, "defaulted"); -`endif + + a = '{}; + i = a.size(); `checkh(i, 0); end begin diff --git a/test_regress/t/t_assoc_pattern_unsup.out b/test_regress/t/t_assoc_pattern_unsup.out deleted file mode 100644 index 05f437fa5..000000000 --- a/test_regress/t/t_assoc_pattern_unsup.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_assoc_pattern_unsup.v:19:11: Unsupported: Assignment pattern applies against non struct/union data type: 'string[string]' - : ... In instance t - 19 | a = '{ "f": "fooed", "b": "bared", default: "defaulted" }; - | ^~ -%Error: Exiting due to diff --git a/test_regress/t/t_assoc_pattern_unsup.pl b/test_regress/t/t_assoc_pattern_unsup.pl deleted file mode 100755 index 63947fd00..000000000 --- a/test_regress/t/t_assoc_pattern_unsup.pl +++ /dev/null @@ -1,19 +0,0 @@ -#!/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(vlt => 1); - -lint( - fails => 1, - expect_filename => $Self->{golden_filename}, - ); - -ok(1); -1; diff --git a/test_regress/t/t_assoc_pattern_unsup.v b/test_regress/t/t_assoc_pattern_unsup.v deleted file mode 100644 index e2f896a0f..000000000 --- a/test_regress/t/t_assoc_pattern_unsup.v +++ /dev/null @@ -1,28 +0,0 @@ -// 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 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*/); - - initial begin - int i; - string a [string]; - string k; - string v; - - a = '{ "f": "fooed", "b": "bared", default: "defaulted" }; - i = a.size(); `checkh(i, 2); // Default doesn't count - v = a["f"]; `checks(v, "fooed"); - v = a["b"]; `checks(v, "bared"); - v = a["NEXISTS"]; `checks(v, "defaulted"); - - $write("*-* All Finished *-*\n"); - $finish; - end -endmodule