Support queue slicing (#2326).

This commit is contained in:
Wilson Snyder 2020-10-18 13:23:02 -04:00
parent ec36d0d772
commit 5d3dd52f13
10 changed files with 189 additions and 34 deletions

View File

@ -4,6 +4,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.103 devel
**** Support queue slicing (#2326).
* Verilator 4.102 2020-10-15

View File

@ -272,6 +272,32 @@ public:
}
~VlQueue() {}
// Standard copy constructor works. Verilog: assoca = assocb
static VlQueue cons(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
}
// METHODS
T_Value& atDefault() { return m_defaultValue; }
@ -352,6 +378,15 @@ public:
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(size_t lsb, size_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (size_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }

View File

@ -1254,7 +1254,10 @@ public:
/// along with all children and next(s). This is often better to use
/// than an immediate deleteTree, as any pointers into this node will
/// persist for the lifetime of the visitor
void pushDeletep(AstNode* nodep) { m_deleteps.push_back(nodep); }
void pushDeletep(AstNode* nodep) {
UASSERT_STATIC(nodep, "Cannot delete nullptr node");
m_deleteps.push_back(nodep);
}
/// Call deleteTree on all previously pushDeletep()'ed nodes
void doDeletes();

View File

@ -4599,6 +4599,28 @@ public:
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
// Children: expression (elements or other queues)
public:
AstConsQueue(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr)
: ASTGEN_SUPER(fl) {
setNOp1p(lhsp);
setNOp2p(rhsp);
}
ASTNODE_NODE_FUNCS(ConsQueue)
virtual string emitVerilog() override { return "'{%l, %r}"; }
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(); } // op1 = expression
AstNode* rhsp() const { return op2p(); } // op2 = expression
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstBegin : public AstNodeBlock {
// A Begin/end named block, only exists shortly after parsing until linking
// Parents: statement

View File

@ -1575,6 +1575,22 @@ class EmitCImp : EmitCStmts {
}
}
virtual void visit(AstConsQueue* nodep) override {
putbs(nodep->dtypep()->cType("", false, false));
if (!nodep->lhsp()) {
puts("()");
} else {
puts("::cons(");
iterateAndNextNull(nodep->lhsp());
if (nodep->rhsp()) {
puts(", ");
putbs("");
}
iterateAndNextNull(nodep->rhsp());
puts(")");
}
}
virtual void visit(AstChangeDet* nodep) override { //
m_blkChangeDetVec.push_back(nodep);
}

View File

@ -484,12 +484,20 @@ private:
// LHS, RHS is self-determined
// signed: Unsigned (11.8.1)
// width: LHS + RHS
AstNodeDType* vdtypep = m_vup->dtypeNullSkipRefp();
if (VN_IS(vdtypep, QueueDType)) {
// Queue "element 0" is lhsp, so we need to swap arguments
auto* newp = new AstConsQueue(nodep->fileline(), nodep->rhsp()->unlinkFrBack(),
nodep->lhsp()->unlinkFrBack());
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
userIterateChildren(newp, m_vup);
return;
}
if (m_vup->prelim()) {
AstNodeDType* vdtypep = m_vup->dtypeNullSkipRefp();
if (vdtypep
&& (VN_IS(vdtypep, AssocArrayDType) //
|| VN_IS(vdtypep, DynArrayDType) //
|| VN_IS(vdtypep, QueueDType))) {
if (VN_IS(vdtypep, AssocArrayDType) //
|| VN_IS(vdtypep, DynArrayDType) //
|| VN_IS(vdtypep, QueueDType)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Concatenation to form "
<< vdtypep->prettyDTypeNameQ() << "data type");
}
@ -609,14 +617,6 @@ private:
// LHS, RHS is self-determined
// width: value(LHS) * width(RHS)
if (m_vup->prelim()) {
AstNodeDType* vdtypep = m_vup->dtypeNullSkipRefp();
if (vdtypep
&& (VN_IS(vdtypep, AssocArrayDType) || VN_IS(vdtypep, DynArrayDType)
|| VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, UnpackArrayDType))) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Replication to form "
<< vdtypep->prettyDTypeNameQ() << " data type");
}
iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change
const AstConst* constp = VN_CAST(nodep->rhsp(), Const);
@ -631,6 +631,26 @@ private:
"1800-2017 11.4.12.1)");
times = 1;
}
AstNodeDType* vdtypep = m_vup->dtypeNullSkipRefp();
if (VN_IS(vdtypep, QueueDType)) {
if (times != 1)
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-1 replication to form "
<< vdtypep->prettyDTypeNameQ()
<< " data type");
// Don't iterate lhsp as SELF, the potential Concat below needs
// the adtypep passed down to recognize the QueueDType
userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, BOTH).p());
nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (VN_IS(vdtypep, AssocArrayDType) || VN_IS(vdtypep, DynArrayDType)
|| VN_IS(vdtypep, UnpackArrayDType)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Replication to form "
<< vdtypep->prettyDTypeNameQ() << " data type");
}
iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
if (nodep->lhsp()->isString()) {
AstNode* newp = new AstReplicateN(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack());
@ -1133,10 +1153,21 @@ private:
}
virtual void visit(AstUnbounded* nodep) override {
nodep->dtypeSetSigned32(); // Used in int context
if (!VN_IS(nodep->backp(), IsUnbounded) && !VN_IS(nodep->backp(), BracketArrayDType)
&& !(VN_IS(nodep->backp(), Var) && VN_CAST(nodep->backp(), Var)->isParam())) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context.");
if (VN_IS(nodep->backp(), IsUnbounded)) return; // Ok, leave
if (VN_IS(nodep->backp(), BracketArrayDType)) return; // Ok, leave
if (auto* varp = VN_CAST(nodep->backp(), Var)) {
if (varp->isParam()) return; // Ok, leave
}
// queue_slice[#:$]
if (auto* selp = VN_CAST(nodep->backp(), SelExtract)) {
if (VN_IS(selp->fromp()->dtypep(), QueueDType)) {
nodep->replaceWith(
new AstConst(nodep->fileline(), AstConst::Signed32(), 0x7FFFFFFF));
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
}
nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context.");
}
virtual void visit(AstIsUnbounded* nodep) override {
if (m_vup->prelim()) {
@ -1928,6 +1959,38 @@ private:
}
nodep->dtypeFrom(nodep->itemp());
}
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");
if (m_vup->prelim()) {
userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, PRELIM).p());
userIterateAndNext(nodep->rhsp(), WidthVP(vdtypep, PRELIM).p());
nodep->dtypeFrom(vdtypep);
}
if (m_vup->final()) {
// Arguments can be either elements of the queue or a queue itself
// Concats (part of tree of concats) must always become ConsQueue's
if (nodep->lhsp()) {
if (VN_IS(nodep->lhsp()->dtypep(), QueueDType)
|| VN_IS(nodep->lhsp(), ConsQueue)) {
userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, FINAL).p());
} else {
// Sub elements are not queues, but concats, must always pass concats down
iterateCheckTyped(nodep, "LHS", nodep->lhsp(), vdtypep->subDTypep(), FINAL);
}
}
if (nodep->rhsp()) {
if (VN_IS(nodep->rhsp()->dtypep(), QueueDType)
|| VN_IS(nodep->rhsp(), ConsQueue)) {
userIterateAndNext(nodep->rhsp(), WidthVP(vdtypep, FINAL).p());
} else {
iterateCheckTyped(nodep, "RHS", nodep->rhsp(), vdtypep->subDTypep(), FINAL);
}
}
nodep->dtypeFrom(vdtypep);
}
}
virtual void visit(AstInitItem* nodep) override { //
userIterateChildren(nodep, m_vup);
}
@ -2490,12 +2553,10 @@ private:
newp->didWidth(true);
newp->makeStatement();
} else {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Queue .delete(index) method, as is O(n) "
"complexity and slow.");
newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
"erase", index_exprp->unlinkFrBack());
newp->protect(false);
newp->didWidth(true);
newp->makeStatement();
}
}
@ -2511,9 +2572,6 @@ private:
newp->protect(false);
newp->makeStatement();
} else {
nodep->v3warn(
E_UNSUPPORTED,
"Unsupported: Queue .insert method, as is O(n) complexity and slow.");
newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
nodep->name(), index_exprp->unlinkFrBack());
newp->addPinsp(argp->exprp()->unlinkFrBack());
@ -2874,10 +2932,12 @@ private:
while (const AstConstDType* vdtypep = VN_CAST(dtypep, ConstDType)) {
dtypep = vdtypep->subDTypep()->skipRefp();
}
if (AstNodeUOrStructDType* vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
if (auto* vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
} else if (AstNodeArrayDType* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
} else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
VL_DO_DANGLING(patternArray(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()) {
VL_DO_DANGLING(patternBasic(nodep, dtypep, defaultp), nodep);
} else {
@ -3038,6 +3098,21 @@ private:
// 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);
for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp;
patp = VN_CAST(patp->nextp(), PatMember)) {
patp->dtypep(arrayp->subDTypep());
AstNode* valuep = patternMemberValueIterate(patp);
auto* newap = new AstConsQueue(nodep->fileline(), valuep, newp);
newap->dtypeFrom(arrayp);
newp = newap;
}
nodep->replaceWith(newp);
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
}
void patternBasic(AstPattern* nodep, AstNodeDType* vdtypep, AstPatMember* defaultp) {
AstBasicDType* bdtypep = VN_CAST(vdtypep, BasicDType);
VNumRange range = bdtypep->declRange();

View File

@ -439,6 +439,15 @@ private:
// if (debug() >= 9) newp->dumpTree(cout, "--SELEXnew: ");
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (VN_IS(ddtypep, QueueDType)) {
auto* newp = new AstCMethodHard(nodep->fileline(), fromp, "slice", msbp);
msbp->addNext(lsbp);
newp->dtypep(ddtypep);
newp->didWidth(true);
newp->protect(false);
UINFO(6, " new " << newp << endl);
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else { // nullptr=bad extract, or unknown node type
nodep->v3error("Illegal range select; type already selected, or bad dimension: "
<< "data type is " << fromdata.m_errp->prettyDTypeNameQ());

View File

@ -3403,8 +3403,7 @@ assignment_pattern<patternp>: // ==IEEE: assignment_pattern
// // also IEEE "''{' array_pattern_key ':' ...
| yP_TICKBRA patternMemberList '}' { $$ = new AstPattern($1,$2); }
// // IEEE: Not in grammar, but in VMM
| yP_TICKBRA '}'
{ $$ = new AstPattern($1, nullptr); $1->v3warn(E_UNSUPPORTED, "Unsupported: Empty '{}"); }
| yP_TICKBRA '}' { $$ = new AstPattern($1, nullptr); }
;
// "datatype id = x {, id = x }" | "yaId = x {, id=x}" is legal

View File

@ -1,4 +0,0 @@
%Error-UNSUPPORTED: t/t_queue_slice.v:21:11: Unsupported: Empty '{}
21 | q = '{};
| ^~
%Error: Exiting due to

View File

@ -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;