mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
Support string putc, getc, substr, bug1606.
Signed-off-by: Wilson Snyder <[email protected]>
This commit is contained in:
committed by
Wilson Snyder
parent
460e0541f3
commit
8cdc0c4e00
@@ -5995,6 +5995,78 @@ public:
|
||||
virtual string emitC() { return "hypot(%li,%ri)"; }
|
||||
};
|
||||
|
||||
class AstPutcN : public AstNodeTriop {
|
||||
// Verilog string.putc()
|
||||
public:
|
||||
AstPutcN(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* ths) : AstNodeTriop(fl, lhsp, rhsp, ths) {
|
||||
dtypeSetString();
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(PutcN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs,
|
||||
const V3Number& rhs, const V3Number& ths) {
|
||||
out.opPutcN(lhs, rhs, ths);
|
||||
}
|
||||
virtual string name() const { return "putc"; }
|
||||
virtual string emitVerilog() { return "%k(%l.putc(%r,%t))"; }
|
||||
virtual string emitC() { return "VL_PUTC_N(%li,%ri,%ti)"; }
|
||||
virtual string emitSimpleOperator() { return ""; }
|
||||
virtual bool cleanOut() const { return true; }
|
||||
virtual bool cleanLhs() const { return true; }
|
||||
virtual bool cleanRhs() const { return true; }
|
||||
virtual bool cleanThs() const { return true; }
|
||||
virtual bool sizeMattersLhs() const { return false; }
|
||||
virtual bool sizeMattersRhs() const { return false; }
|
||||
virtual bool sizeMattersThs() const { return false; }
|
||||
};
|
||||
|
||||
class AstGetcN : public AstNodeBiop {
|
||||
// Verilog string.getc()
|
||||
public:
|
||||
AstGetcN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetBitSized(8, AstNumeric::UNSIGNED);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(GetcN)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) {
|
||||
return new AstGetcN(this->fileline(), lhsp, rhsp);
|
||||
}
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) {
|
||||
out.opGetcN(lhs, rhs);
|
||||
}
|
||||
virtual string name() const { return "getc"; }
|
||||
virtual string emitVerilog() { return "%k(%l.getc(%r))"; }
|
||||
virtual string emitC() { return "VL_GETC_N(%li,%ri)"; }
|
||||
virtual string emitSimpleOperator() { return ""; }
|
||||
virtual bool cleanOut() const { return true; }
|
||||
virtual bool cleanLhs() const { return true; }
|
||||
virtual bool cleanRhs() const { return true; }
|
||||
virtual bool sizeMattersLhs() const { return false; }
|
||||
virtual bool sizeMattersRhs() const { return false; }
|
||||
};
|
||||
|
||||
class AstSubstrN : public AstNodeTriop {
|
||||
// Verilog string.substr()
|
||||
public:
|
||||
AstSubstrN(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* ths) : AstNodeTriop(fl, lhsp, rhsp, ths) {
|
||||
dtypeSetString();
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SubstrN)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs,
|
||||
const V3Number& rhs, const V3Number& ths) {
|
||||
out.opSubstrN(lhs, rhs, ths);
|
||||
}
|
||||
virtual string name() const { return "substr"; }
|
||||
virtual string emitVerilog() { return "%k(%l.substr(%r,%t))"; }
|
||||
virtual string emitC() { return "VL_SUBSTR_N(%li,%ri,%ti)"; }
|
||||
virtual string emitSimpleOperator() { return ""; }
|
||||
virtual bool cleanOut() const { return true; }
|
||||
virtual bool cleanLhs() const { return true; }
|
||||
virtual bool cleanRhs() const { return true; }
|
||||
virtual bool cleanThs() const { return true; }
|
||||
virtual bool sizeMattersLhs() const { return false; }
|
||||
virtual bool sizeMattersRhs() const { return false; }
|
||||
virtual bool sizeMattersThs() const { return false; }
|
||||
};
|
||||
|
||||
class AstCompareNN : public AstNodeBiop {
|
||||
// Verilog str.compare() and str.icompare()
|
||||
private:
|
||||
|
||||
@@ -2526,6 +2526,8 @@ private:
|
||||
TREEOPV("AstLogIf{$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}");
|
||||
TREEOPV("AstLogEq{$lhsp, $rhsp}", "replaceLogEq(nodep)");
|
||||
// Strings
|
||||
TREEOPC("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
|
||||
TREEOPC("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
|
||||
TREEOPC("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_CAST(nodep->lhsp(), Const)->num().toString())");
|
||||
|
||||
|
||||
|
||||
@@ -643,6 +643,10 @@ public:
|
||||
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeTriop* nodep) {
|
||||
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
|
||||
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
|
||||
}
|
||||
virtual void visit(AstRedXor* nodep) {
|
||||
if (nodep->lhsp()->isWide()) {
|
||||
visit(VN_CAST(nodep, NodeUniop));
|
||||
|
||||
@@ -62,6 +62,18 @@ class EmitCInlines : EmitCBaseVisitor {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstPutcN* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstGetcN* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSubstrN* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCompareNN* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
|
||||
@@ -1262,6 +1262,44 @@ V3Number& V3Number::opAtoN(const V3Number& lhs, int base) {
|
||||
return setLongS(static_cast<vlsint32_t>(v));
|
||||
}
|
||||
|
||||
V3Number& V3Number::opPutcN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths) {
|
||||
NUM_ASSERT_OP_ARGS3(lhs, rhs, ths);
|
||||
NUM_ASSERT_STRING_ARGS1(lhs);
|
||||
string lstring = lhs.toString();
|
||||
const vlsint32_t i = rhs.toSInt();
|
||||
const vlsint32_t c = ths.toSInt() & 0xFF;
|
||||
// 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0
|
||||
// when evaluating the second condition, i must be positive.
|
||||
if (0 <= i && static_cast<uint32_t>(i) < lstring.length() && c != 0) lstring[i] = c;
|
||||
return setString(lstring);
|
||||
}
|
||||
|
||||
V3Number& V3Number::opGetcN(const V3Number& lhs, const V3Number& rhs) {
|
||||
NUM_ASSERT_OP_ARGS2(lhs, rhs);
|
||||
NUM_ASSERT_STRING_ARGS1(lhs);
|
||||
const string lstring = lhs.toString();
|
||||
const vlsint32_t i = rhs.toSInt();
|
||||
vlsint32_t v = 0;
|
||||
// 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len()
|
||||
// when evaluating the second condition, i must be positive.
|
||||
if (0 <= i && static_cast<uint32_t>(i) < lstring.length()) v = lstring[i];
|
||||
return setLong(v);
|
||||
}
|
||||
|
||||
V3Number& V3Number::opSubstrN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths) {
|
||||
NUM_ASSERT_OP_ARGS3(lhs, rhs, ths);
|
||||
NUM_ASSERT_STRING_ARGS1(lhs);
|
||||
const string lstring = lhs.toString();
|
||||
const vlsint32_t i = rhs.toSInt();
|
||||
const vlsint32_t j = ths.toSInt();
|
||||
// 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len()
|
||||
// when evaluating the third condition, j must be positive because 0 <= i <= j is guaranteed by
|
||||
// the former two conditions.
|
||||
if (i < 0 || j < i || static_cast<uint32_t>(j) >= lstring.length()) return setString("");
|
||||
// The second parameter of std::string::substr(i, n) is length, not position as SystemVerilog.
|
||||
return setString(lstring.substr(i, j - i + 1));
|
||||
}
|
||||
|
||||
V3Number& V3Number::opCompareNN(const V3Number& lhs, const V3Number& rhs, bool ignoreCase) {
|
||||
NUM_ASSERT_OP_ARGS2(lhs, rhs);
|
||||
NUM_ASSERT_STRING_ARGS2(lhs, rhs);
|
||||
|
||||
@@ -361,6 +361,9 @@ public:
|
||||
|
||||
// "N" - string operations
|
||||
V3Number& opAtoN (const V3Number& lhs, int base);
|
||||
V3Number& opPutcN (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
V3Number& opGetcN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opSubstrN (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
V3Number& opCompareNN(const V3Number& lhs,const V3Number& rhs, bool ignoreCase);
|
||||
V3Number& opConcatN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opReplN (const V3Number& lhs, const V3Number& rhs);
|
||||
|
||||
+60
-3
@@ -333,6 +333,38 @@ private:
|
||||
|
||||
// Output integer, input string
|
||||
virtual void visit(AstLenN* nodep) { visit_Os32_string(nodep); }
|
||||
virtual void visit(AstPutcN* nodep) {
|
||||
// CALLER: str.putc()
|
||||
UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
|
||||
if (m_vup && m_vup->prelim()) {
|
||||
// See similar handling in visit_cmp_eq_gt where created
|
||||
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
|
||||
iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
|
||||
iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
|
||||
nodep->dtypeSetString(); //AstPutcN returns the new string to be assigned by AstAssign
|
||||
}
|
||||
}
|
||||
virtual void visit(AstGetcN* nodep) {
|
||||
// CALLER: str.getc()
|
||||
UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
|
||||
if (m_vup && m_vup->prelim()) {
|
||||
// See similar handling in visit_cmp_eq_gt where created
|
||||
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
|
||||
iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
|
||||
nodep->dtypeSetBitSized(8, AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSubstrN* nodep) {
|
||||
// CALLER: str.substr()
|
||||
UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
|
||||
if (m_vup && m_vup->prelim()) {
|
||||
// See similar handling in visit_cmp_eq_gt where created
|
||||
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
|
||||
iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
|
||||
iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
|
||||
nodep->dtypeSetString();
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCompareNN* nodep) {
|
||||
// CALLER: str.compare(), str.icompare()
|
||||
// Widths: 32 bit out
|
||||
@@ -2115,6 +2147,34 @@ private:
|
||||
AstNode* rhs = argp->exprp()->unlinkFrBack();
|
||||
AstNode* newp = new AstCompareNN(nodep->fileline(), lhs, rhs, ignoreCase);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (nodep->name() == "putc" ) {
|
||||
methodOkArguments(nodep, 2, 2);
|
||||
AstArg* arg0p = VN_CAST(nodep->pinsp(), Arg);
|
||||
AstArg* arg1p = VN_CAST(arg0p->nextp(), Arg);
|
||||
AstNodeVarRef* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), VarRef);
|
||||
AstNode* rhsp = arg0p->exprp()->unlinkFrBack();
|
||||
AstNode* thsp = arg1p->exprp()->unlinkFrBack();
|
||||
AstVarRef* varrefp = new AstVarRef(nodep->fileline(), fromp->varp(), false);
|
||||
AstNode* newp = new AstAssign(nodep->fileline(), fromp,
|
||||
new AstPutcN(nodep->fileline(), varrefp, rhsp, thsp));
|
||||
fromp->lvalue(true);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (nodep->name() == "getc") {
|
||||
methodOkArguments(nodep, 1, 1);
|
||||
AstArg* arg0p = VN_CAST(nodep->pinsp(), Arg);
|
||||
AstNode* lhsp = nodep->fromp()->unlinkFrBack();
|
||||
AstNode* rhsp = arg0p->exprp()->unlinkFrBack();
|
||||
AstNode* newp = new AstGetcN(nodep->fileline(), lhsp, rhsp);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (nodep->name() == "substr") {
|
||||
methodOkArguments(nodep, 2, 2);
|
||||
AstArg* arg0p = VN_CAST(nodep->pinsp(), Arg);
|
||||
AstArg* arg1p = VN_CAST(arg0p->nextp(), Arg);
|
||||
AstNode* lhsp = nodep->fromp()->unlinkFrBack();
|
||||
AstNode* rhsp = arg0p->exprp()->unlinkFrBack();
|
||||
AstNode* thsp = arg1p->exprp()->unlinkFrBack();
|
||||
AstNode* newp = new AstSubstrN(nodep->fileline(), lhsp, rhsp, thsp);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (nodep->name() == "atobin"
|
||||
|| nodep->name() == "atohex"
|
||||
|| nodep->name() == "atoi"
|
||||
@@ -2130,9 +2190,6 @@ private:
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
AstNode* newp = new AstAtoN(nodep->fileline(), nodep->fromp()->unlinkFrBack(), fmt);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (nodep->name() == "getc"
|
||||
|| nodep->name() == "putc") {
|
||||
nodep->v3error("Unsupported: built-in string method "<<nodep->prettyNameQ());
|
||||
} else {
|
||||
nodep->v3error("Unknown built-in string method "<<nodep->prettyNameQ());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user