From 038d57070b539b2da2658edd7740d41ea069fd4f Mon Sep 17 00:00:00 2001 From: Arkadiusz Kozdra Date: Fri, 14 Oct 2022 14:55:55 +0200 Subject: [PATCH] Support standalone 'this' in classes (#3675) (#2594) (#3248) --- src/V3AstNodeMath.h | 16 +++++++++++++++ src/V3EmitCFunc.h | 6 ++++++ src/V3LinkDot.cpp | 33 ++++++++++++++++++++++-------- src/V3Width.cpp | 4 ++++ test_regress/t/t_class_uses_this.v | 16 +++++++++++++++ 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/V3AstNodeMath.h b/src/V3AstNodeMath.h index dc2d6f8ad..e6d752bc1 100644 --- a/src/V3AstNodeMath.h +++ b/src/V3AstNodeMath.h @@ -1273,6 +1273,22 @@ public: bool cleanOut() const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; } }; +class AstThisRef final : public AstNodeMath { + // Reference to 'this'. + // @astgen op1 := childDTypep : Optional[AstClassRefDType] // dtype of the node +public: + explicit AstThisRef(FileLine* fl, AstClassRefDType* dtypep) + : ASTGEN_SUPER_ThisRef(fl) { + childDTypep(dtypep); + } + ASTGEN_MEMBERS_AstThisRef; + string emitC() override { return "this"; } + string emitVerilog() override { return "this"; } + bool same(const AstNode* /*samep*/) const override { return true; } + bool cleanOut() const override { return true; } + AstNodeDType* getChildDTypep() const override { return childDTypep(); } + virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } +}; class AstUCFunc final : public AstNodeMath { // User's $c function // Perhaps this should be an AstNodeListop; but there's only one list math right now diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 8369386bd..28cd0b9e8 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1168,6 +1168,12 @@ public: emitConstant(nodep, nullptr, ""); } } + void visit(AstThisRef* nodep) override { + putbs(nodep->dtypep()->cType("", false, false)); + puts("{"); + puts(m_useSelfForThis ? "vlSelf" : "this"); + puts("}"); + } // void visit(AstMTaskBody* nodep) override { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 0611e6297..1f4757e89 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2054,6 +2054,13 @@ private: } return false; } + VSymEnt* getThisClassSymp() { + VSymEnt* classSymp = m_ds.m_dotSymp; + do { + classSymp = classSymp->parentp(); + } while (classSymp && !VN_IS(classSymp->nodep(), Class)); + return classSymp; + } // VISITs void visit(AstNetlist* nodep) override { @@ -2201,10 +2208,7 @@ private: m_ds.m_dotPos = DP_SCOPE; if (VN_IS(nodep->lhsp(), ParseRef) && nodep->lhsp()->name() == "this") { - VSymEnt* classSymp = m_ds.m_dotSymp; - do { - classSymp = classSymp->parentp(); - } while (classSymp && !VN_IS(classSymp->nodep(), Class)); + VSymEnt* classSymp = getThisClassSymp(); if (!classSymp) { nodep->v3error("'this' used outside class (IEEE 1800-2017 8.11)"); m_ds.m_dotErr = true; @@ -2213,10 +2217,7 @@ private: UINFO(8, " this. " << m_ds.ascii() << endl); } } else if (VN_IS(nodep->lhsp(), ParseRef) && nodep->lhsp()->name() == "super") { - const VSymEnt* classSymp = m_ds.m_dotSymp; - do { - classSymp = classSymp->parentp(); - } while (classSymp && !VN_IS(classSymp->nodep(), Class)); + const VSymEnt* classSymp = getThisClassSymp(); if (!classSymp) { nodep->v3error("'super' used outside class (IEEE 1800-2017 8.15)"); m_ds.m_dotErr = true; @@ -2310,6 +2311,22 @@ private: nodep->v3warn(E_UNSUPPORTED, "Unsupported: super"); m_ds.m_dotErr = true; } + if (nodep->name() == "this") { + iterateChildren(nodep); + if (m_statep->forPrimary()) return; // The class might be parametrized somewhere + const VSymEnt* classSymp = getThisClassSymp(); + if (!classSymp) { + nodep->v3error("'this' used outside class (IEEE 1800-2017 8.11)"); + return; + } + AstClass* const classp = VN_AS(classSymp->nodep(), Class); + AstClassRefDType* const dtypep + = new AstClassRefDType{nodep->fileline(), classp, nullptr}; + AstThisRef* const newp = new AstThisRef{nodep->fileline(), dtypep}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } if (m_ds.m_dotPos == DP_FINAL && VN_IS(m_ds.m_unlinkedScopep, LambdaArgRef) && nodep->name() == "index") { // 'with' statement's 'item.index' diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 6717b91e6..ffb9e8d08 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2465,6 +2465,10 @@ private: userIterateChildren(nodep, nullptr); // First size all members nodep->repairCache(); } + void visit(AstThisRef* nodep) override { + if (nodep->didWidthAndSet()) return; + nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep())); + } void visit(AstClassRefDType* nodep) override { if (nodep->didWidthAndSet()) return; // TODO this maybe eventually required to properly resolve members, diff --git a/test_regress/t/t_class_uses_this.v b/test_regress/t/t_class_uses_this.v index 531c6e1cb..966f8bb23 100644 --- a/test_regress/t/t_class_uses_this.v +++ b/test_regress/t/t_class_uses_this.v @@ -11,13 +11,25 @@ class Cls; this.addr = addr; end : body endfunction + function void set2(bit [3:0] addr); + begin : body + Cls c2 = this; + c2.addr = addr; + end : body + endfunction extern function void setext(bit [3:0] addr); + extern function void setext2(bit [3:0] addr); endclass function void Cls::setext(bit [3:0] addr); this.addr = addr; endfunction +function void Cls::setext2(bit [3:0] addr); + Cls c2 = this; + c2.addr = addr; +endfunction + module t(/*AUTOARG*/ // Inputs clk @@ -34,8 +46,12 @@ module t(/*AUTOARG*/ $display(baz.addr); `endif if (bar.addr != 4) $stop; + bar.set2(1); + if (bar.addr != 1) $stop; bar.setext(2); if (bar.addr != 2) $stop; + bar.setext2(3); + if (bar.addr != 3) $stop; $write("*-* All Finished *-*\n"); $finish; end