diff --git a/include/verilatedos.h b/include/verilatedos.h index c84e13ef0..c5677b992 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -563,6 +563,7 @@ template T const& as_const(T& v) { return v; } + }; // namespace vlstd //========================================================================= diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 81b059442..7459ce773 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -44,7 +44,7 @@ constexpr int MAX_SPRINTF_DOUBLE_SIZE "Number operation called with same source and dest") #define NUM_ASSERT_LOGIC_ARGS1(arg1) \ - UASSERT((!(arg1).isDouble() && !(arg1).isString()), \ + UASSERT(((arg1).dataType() == V3NumberData::V3NumberDataType::LOGIC), \ "Number operation called with non-logic (double or string) argument: '" << (arg1) \ << '"') #define NUM_ASSERT_LOGIC_ARGS2(arg1, arg2) \ @@ -79,8 +79,10 @@ void V3Number::v3errorEnd(std::ostringstream& str) const { nsstr << str.str(); if (m_nodep) { m_nodep->v3errorEnd(nsstr); - } else { + } else if (m_fileline) { m_fileline->v3errorEnd(nsstr); + } else { + V3Error::v3errorEnd(nsstr); } } @@ -96,13 +98,11 @@ void V3Number::v3errorEndFatal(std::ostringstream& str) const { V3Number::V3Number(VerilogStringLiteral, AstNode* nodep, const string& str) { // Create a number using a verilog string as the value, thus 8 bits per character. - // cppcheck bug - doesn't see init() resets these - // cppcheck: Member variable 'm_sized/m_width' is not initialized in the constructor - init(nodep, str.length() * 8); - m_fromString = true; + init(nodep, std::max(str.length() * 8, 1)); + m_data.m_fromString = true; for (unsigned pos = 0; pos < str.length(); ++pos) { const int topos = str.length() - 1 - pos; - ValueAndX& v = m_value[topos / 4]; + ValueAndX& v = m_data.num()[topos / 4]; for (int bit = 0; bit < 8; ++bit) { if (str[pos] & (1UL << bit)) { v.m_value |= (1UL << (bit + (topos % 4) * 8)); } } @@ -112,7 +112,7 @@ V3Number::V3Number(VerilogStringLiteral, AstNode* nodep, const string& str) { V3Number::V3Number(AstNode* nodep, const AstNodeDType* nodedtypep) { if (nodedtypep->isString()) { - init(nodep, 0); + init(nodep); setString(""); } else if (nodedtypep->isDouble()) { init(nodep, 64); @@ -124,6 +124,7 @@ V3Number::V3Number(AstNode* nodep, const AstNodeDType* nodedtypep) { void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl) { init(nodep, 0); + m_data.setLogic(); m_fileline = fl; const char* value_startp = sourcep; for (const char* cp = sourcep; *cp; cp++) { @@ -172,35 +173,35 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl) base = 'd'; } - for (int i = 0; i < words(); ++i) m_value[i] = {0, 0}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0}; // Special SystemVerilog unsized constructs if (base == '0') { + width(1, false); // So we extend it setBit(0, 0); - width(1, false); // So we extend it - m_autoExtend = true; + m_data.m_autoExtend = true; } else if (base == '1') { + width(1, false); // So we extend it setBit(0, 1); - width(1, false); // So we extend it - m_autoExtend = true; + m_data.m_autoExtend = true; } else if (tolower(base) == 'z') { + width(1, false); // So we extend it setBit(0, 'z'); - width(1, false); // So we extend it - m_autoExtend = true; + m_data.m_autoExtend = true; } else if (tolower(base) == 'x') { - setBit(0, 'x'); width(1, false); // So we extend it - m_autoExtend = true; + setBit(0, 'x'); + m_data.m_autoExtend = true; } // Otherwise... - else if (!m_sized) { + else if (!sized()) { width(32, false); // Says IEEE 1800-2012 5.7.1 if (unbased) isSigned(true); // Also says the spec. } // Ignore leading blanks while (*value_startp == '_' || isspace(*value_startp)) value_startp++; - if (!*value_startp && !m_autoExtend) { + if (!*value_startp && !m_data.m_autoExtend) { v3error("Number is missing value digits: " << sourcep); } @@ -229,7 +230,7 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl) if (olen <= 7) { // 10000000 fits in 32 bits, so ok // Constants are common, so for speed avoid wide math until we need it val = val * 10 + (*cp - '0'); - m_value[0].m_value = val; + m_data.num()[0].m_value = val; } else { // Wide; all previous digits are already in m_value[0] // this = (this * 10)/*product*/ + (*cp-'0')/*addend*/ // Assumed rare; lots of optimizations are possible here @@ -237,12 +238,12 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl) const V3Number ten(this, width() + 4, 10); const V3Number addend(this, width(), (*cp - '0')); product.opMul(*this, ten); - this->opAdd(product, addend); + opAdd(product, addend); if (product.bitsValue(width(), 4)) { // Overflowed static int warned = 0; v3error("Too many digits for " << width() << " bit number: " << sourcep << '\n' - << ((!m_sized && !warned++) ? ( + << ((!sized() && !warned++) ? ( V3Error::warnMore() + "... As that number was unsized" + " ('d...) it is limited to 32 bits (IEEE 1800-2017 " "5.7.1)\n" @@ -387,72 +388,72 @@ int V3Number::log2b(uint32_t num) { // Setters V3Number& V3Number::setZero() { - for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; + for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; return *this; } V3Number& V3Number::setQuad(uint64_t value) { - for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; - m_value[0].m_value = value & 0xffffffffULL; - if (width() > 32) m_value[1].m_value = (value >> 32ULL) & 0xffffffffULL; + for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; + m_data.num()[0].m_value = value & 0xffffffffULL; + if (width() > 32) m_data.num()[1].m_value = (value >> 32ULL) & 0xffffffffULL; opCleanThis(); return *this; } V3Number& V3Number::setLong(uint32_t value) { - for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; - m_value[0].m_value = value; + for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; + m_data.num()[0].m_value = value; opCleanThis(); return *this; } V3Number& V3Number::setLongS(int32_t value) { - for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; + for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; union { uint32_t u; int32_t s; } u; u.s = value; if (u.s) {} - m_value[0].m_value = u.u; + m_data.num()[0].m_value = u.u; opCleanThis(); return *this; } V3Number& V3Number::setDouble(double value) { if (VL_UNCOVERABLE(width() != 64)) v3fatalSrc("Real operation on wrong sized number"); - m_double = true; + m_data.setDouble(); union { double d; uint32_t u[2]; } u; u.d = value; if (u.d != 0.0) {} - for (int i = 2; i < words(); i++) m_value[i] = {0, 0}; - m_value[0].m_value = u.u[0]; - m_value[1].m_value = u.u[1]; + for (int i = 2; i < words(); i++) m_data.num()[i] = {0, 0}; + m_data.num()[0].m_value = u.u[0]; + m_data.num()[1].m_value = u.u[1]; return *this; } V3Number& V3Number::setSingleBits(char value) { - for (int i = 1 /*upper*/; i < words(); i++) m_value[i] = {0, 0}; - m_value[0] = {(value == '1' || value == 'x' || value == 1 || value == 3), - (value == 'z' || value == 'x' || value == 2 || value == 3)}; + for (int i = 1 /*upper*/; i < words(); i++) m_data.num()[i] = {0, 0}; + m_data.num()[0] = {(value == '1' || value == 'x' || value == 1 || value == 3), + (value == 'z' || value == 'x' || value == 2 || value == 3)}; return *this; } V3Number& V3Number::setAllBits0() { - for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; + for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; return *this; } V3Number& V3Number::setAllBits1() { - for (int i = 0; i < words(); i++) m_value[i] = {~0U, 0}; + for (int i = 0; i < words(); i++) m_data.num()[i] = {~0U, 0}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsX() { // Use setAllBitsXRemoved if calling this based on a non-X/Z input value such as divide by zero - for (int i = 0; i < words(); i++) m_value[i] = {~0U, ~0U}; + for (int i = 0; i < words(); i++) m_data.num()[i] = {~0U, ~0U}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsZ() { - for (int i = 0; i < words(); i++) m_value[i] = {0, ~0U}; + for (int i = 0; i < words(); i++) m_data.num()[i] = {0, ~0U}; opCleanThis(); return *this; } @@ -493,7 +494,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const { } else if (isString()) { return '"' + toString() + '"'; } else { - if (VL_UNCOVERABLE((m_value[words() - 1].m_value | m_value[words() - 1].m_valueX) + if (VL_UNCOVERABLE((m_data.num()[words() - 1].m_value | m_data.num()[words() - 1].m_valueX) & ~hiWordMask())) { out << "%E-hidden-bits"; // LCOV_EXCL_LINE } @@ -688,7 +689,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { } case 's': { // Spec says always drop leading zeros, this isn't quite right, we space pad. - int bit = this->width() - 1; + int bit = width() - 1; bool start = true; while ((bit % 8) != 7) bit++; for (; bit >= 0; bit -= 8) { @@ -709,7 +710,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { case 'd': { // Unsigned decimal const bool issigned = (code == '~'); if (fmtsize == "") { - const double mantissabits = this->width() - (issigned ? 1 : 0); + const double mantissabits = width() - (issigned ? 1 : 0); // To get the number of digits required, we want to compute // log10(2**mantissabits) and round it up. To be able to handle // a very wide mantissa, we use log2(2**mantissabits)/log2(10), @@ -765,7 +766,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { // 'p' // Packed - converted to another code by V3Width case 'u': { // Packed 2-state for (int i = 0; i < words(); i++) { - const uint32_t v = m_value[i].m_value; + const uint32_t v = m_data.num()[i].m_value; str += static_cast((v >> 0) & 0xff); str += static_cast((v >> 8) & 0xff); str += static_cast((v >> 16) & 0xff); @@ -775,7 +776,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { } case 'z': { // Packed 4-state for (int i = 0; i < words(); i++) { - const ValueAndX v = m_value[i]; + const ValueAndX v = m_data.num()[i]; str += static_cast((v.m_value >> 0) & 0xff); str += static_cast((v.m_value >> 8) & 0xff); str += static_cast((v.m_value >> 16) & 0xff); @@ -872,12 +873,12 @@ uint32_t V3Number::toUInt() const { UASSERT(!isFourState(), "toUInt with 4-state " << *this); // We allow wide numbers that represent values <= 32 bits for (int i = 1; i < words(); ++i) { - if (m_value[i].m_value) { + if (m_data.num()[i].m_value) { v3error("Value too wide for 32-bits expected in this context " << *this); break; } } - return m_value[0].m_value; + return m_data.num()[0].m_value; } double V3Number::toDouble() const { @@ -888,8 +889,8 @@ double V3Number::toDouble() const { double d; uint32_t u[2]; } u; - u.u[0] = m_value[0].m_value; - u.u[1] = m_value[1].m_value; + u.u[0] = m_data.num()[0].m_value; + u.u[1] = m_data.num()[1].m_value; return u.d; } @@ -911,14 +912,14 @@ uint64_t V3Number::toUQuad() const { // We allow wide numbers that represent values <= 64 bits if (isDouble()) return static_cast(toDouble()); for (int i = 2; i < words(); ++i) { - if (m_value[i].m_value) { + if (m_data.num()[i].m_value) { v3error("Value too wide for 64-bits expected in this context " << *this); break; } } if (width() <= 32) return (static_cast(toUInt())); - return ((static_cast(m_value[1].m_value) << 32ULL) - | (static_cast(m_value[0].m_value))); + return ((static_cast(m_data.num()[1].m_value) << 32ULL) + | (static_cast(m_data.num()[0].m_value))); } int64_t V3Number::toSQuad() const { @@ -932,8 +933,8 @@ int64_t V3Number::toSQuad() const { string V3Number::toString() const { UASSERT(!isFourState(), "toString with 4-state " << *this); // Spec says always drop leading zeros, this isn't quite right, we space pad. - if (isString()) return m_stringVal; - int bit = this->width() - 1; + if (isString()) return m_data.str(); + int bit = width() - 1; bool start = true; while ((bit % 8) != 7) bit++; string str; @@ -948,14 +949,18 @@ string V3Number::toString() const { } V3Hash V3Number::toHash() const { - V3Hash hash(m_width); - for (int i = 0; i < words(); ++i) { hash += m_value[i].m_value; } + V3Hash hash{width()}; + if (isString()) { + hash += V3Hash{m_data.str()}; + } else { + for (int i = 0; i < words(); ++i) { hash += m_data.num()[i].m_value; } + } return hash; } uint32_t V3Number::edataWord(int eword) const { UASSERT(!isFourState(), "edataWord with 4-state " << *this); - return m_value[eword].m_value; + return m_data.num()[eword].m_value; } uint8_t V3Number::dataByte(int byte) const { @@ -963,30 +968,34 @@ uint8_t V3Number::dataByte(int byte) const { } bool V3Number::isAllZ() const { + if (isDouble() || isString()) return false; for (int i = 0; i < width(); i++) { if (!bitIsZ(i)) return false; } return true; } bool V3Number::isAllX() const { + if (isDouble() || isString()) return false; uint32_t mask = hiWordMask(); for (int i = words() - 1; i >= 0; --i) { - const ValueAndX v = m_value[i]; + const ValueAndX v = m_data.num()[i]; if ((v.m_value & v.m_valueX) ^ mask) return false; mask = ~0U; } return true; } bool V3Number::isEqZero() const { + if (isString()) return m_data.str().empty(); for (int i = 0; i < words(); i++) { - const ValueAndX v = m_value[i]; + const ValueAndX v = m_data.num()[i]; if (v.m_value || v.m_valueX) return false; } return true; } bool V3Number::isNeqZero() const { + if (isString()) return !m_data.str().empty(); for (int i = 0; i < words(); i++) { - const ValueAndX v = m_value[i]; + const ValueAndX v = m_data.num()[i]; if (v.m_value & ~v.m_valueX) return true; } return false; @@ -998,9 +1007,9 @@ bool V3Number::isBitsZero(int msb, int lsb) const { return true; } bool V3Number::isEqOne() const { - if (m_value[0].m_value != 1 || m_value[0].m_valueX) return false; + if (m_data.num()[0].m_value != 1 || m_data.num()[0].m_valueX) return false; for (int i = 1; i < words(); i++) { - const ValueAndX v = m_value[i]; + const ValueAndX v = m_data.num()[i]; if (v.m_value || v.m_valueX) return false; } return true; @@ -1015,7 +1024,7 @@ bool V3Number::isEqAllOnes(int optwidth) const { bool V3Number::isFourState() const { if (isDouble() || isString()) return false; for (int i = 0; i < words(); ++i) { - if (m_value[i].m_valueX) return true; + if (m_data.num()[i].m_valueX) return true; } return false; } @@ -1036,10 +1045,10 @@ bool V3Number::isAnyZ() const { } bool V3Number::isLtXZ(const V3Number& rhs) const { // Include X/Z in comparisons for sort ordering - for (int bit = 0; bit < std::max(this->width(), rhs.width()); bit++) { - if (this->bitIs1(bit) && rhs.bitIs0(bit)) return true; - if (rhs.bitIs1(bit) && this->bitIs0(bit)) return false; - if (this->bitIsXZ(bit)) return true; + for (int bit = 0; bit < std::max(width(), rhs.width()); bit++) { + if (bitIs1(bit) && rhs.bitIs0(bit)) return true; + if (rhs.bitIs1(bit) && bitIs0(bit)) return false; + if (bitIsXZ(bit)) return true; if (rhs.bitIsXZ(bit)) return false; } return false; @@ -1070,7 +1079,7 @@ int V3Number::widthMin() const { uint32_t V3Number::countBits(const V3Number& ctrl) const { int n = 0; - for (int bit = 0; bit < this->width(); ++bit) { + for (int bit = 0; bit < width(); ++bit) { switch (ctrl.bitIs(0)) { case '0': if (bitIs0(bit)) ++n; @@ -1101,14 +1110,14 @@ uint32_t V3Number::countBits(const V3Number& ctrl1, const V3Number& ctrl2, uint32_t V3Number::countOnes() const { int n = 0; - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (bitIs1(bit)) n++; } return n; } uint32_t V3Number::mostSetBitP1() const { - for (int bit = this->width() - 1; bit >= 0; bit--) { + for (int bit = width() - 1; bit >= 0; bit--) { if (bitIs1(bit)) return bit + 1; } return 0; @@ -1120,7 +1129,7 @@ V3Number& V3Number::opBitsNonX(const V3Number& lhs) { // 0/1->1, X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIs0(bit) || lhs.bitIs1(bit)) setBit(bit, 1); } return *this; @@ -1130,7 +1139,7 @@ V3Number& V3Number::opBitsOne(const V3Number& lhs) { // 1->1, 0/X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIs1(bit)) setBit(bit, 1); } return *this; @@ -1140,7 +1149,7 @@ V3Number& V3Number::opBitsXZ(const V3Number& lhs) { // 0/1->1, X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIsXZ(bit)) setBit(bit, 1); } return *this; @@ -1150,7 +1159,7 @@ V3Number& V3Number::opBitsZ(const V3Number& lhs) { // 0/1->1, X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIsZ(bit)) setBit(bit, 1); } return *this; @@ -1160,7 +1169,7 @@ V3Number& V3Number::opBitsNonZ(const V3Number& lhs) { // 0/1->1, X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (!lhs.bitIsZ(bit)) setBit(bit, 1); } return *this; @@ -1226,7 +1235,7 @@ V3Number& V3Number::opCountBits(const V3Number& expr, const V3Number& ctrl1, con NUM_ASSERT_OP_ARGS4(expr, ctrl1, ctrl2, ctrl3); NUM_ASSERT_LOGIC_ARGS4(expr, ctrl1, ctrl2, ctrl3); setZero(); - m_value[0].m_value = expr.countBits(ctrl1, ctrl2, ctrl3); + m_data.num()[0].m_value = expr.countBits(ctrl1, ctrl2, ctrl3); opCleanThis(); return *this; } @@ -1235,7 +1244,7 @@ V3Number& V3Number::opCountOnes(const V3Number& lhs) { NUM_ASSERT_LOGIC_ARGS1(lhs); if (lhs.isFourState()) return setAllBitsX(); setZero(); - m_value[0].m_value = lhs.countOnes(); + m_data.num()[0].m_value = lhs.countOnes(); opCleanThis(); return *this; } @@ -1292,7 +1301,7 @@ V3Number& V3Number::opNot(const V3Number& lhs) { NUM_ASSERT_LOGIC_ARGS1(lhs); // op i, L(lhs) bit return setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIs0(bit)) { setBit(bit, 1); } else if (lhs.bitIsXZ(bit)) { @@ -1307,7 +1316,7 @@ V3Number& V3Number::opAnd(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit, 1); } else if (lhs.bitIs0(bit) || rhs.bitIs0(bit)) { // 0 @@ -1323,7 +1332,7 @@ V3Number& V3Number::opOr(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIs1(bit) || rhs.bitIs1(bit)) { setBit(bit, 1); } else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) { @@ -1340,7 +1349,7 @@ V3Number& V3Number::opXor(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); setZero(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { setBit(bit, 1); } else if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { @@ -1619,9 +1628,9 @@ bool V3Number::isCaseEq(const V3Number& rhs) const { // i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend. if (isString()) return toString() == rhs.toString(); if (isDouble()) return toDouble() == rhs.toDouble(); - if (this->width() != rhs.width()) return false; + if (width() != rhs.width()) return false; for (int i = 0; i < words(); ++i) { - if (!(m_value[i] == rhs.m_value[i])) return false; + if (!(m_data.num()[i] == rhs.m_data.num()[i])) return false; } return true; } @@ -1754,7 +1763,7 @@ V3Number& V3Number::opShiftR(const V3Number& lhs, const V3Number& rhs) { } const uint32_t rhsval = rhs.toUInt(); if (rhsval < static_cast(lhs.width())) { - for (int bit = 0; bit < this->width(); bit++) setBit(bit, lhs.bitIs(bit + rhsval)); + for (int bit = 0; bit < width(); bit++) setBit(bit, lhs.bitIs(bit + rhsval)); } return *this; } @@ -1768,7 +1777,7 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t if (rhs.isFourState()) return setAllBitsX(); setZero(); for (int bit = 32; bit < rhs.width(); bit++) { - for (int sbit = 0; sbit < this->width(); sbit++) { + for (int sbit = 0; sbit < width(); sbit++) { setBit(sbit, lhs.bitIs(lbits - 1)); // 0/1/X/Z } if (rhs.bitIs1(lbits - 1)) setAllBits1(); // -1 else 0 @@ -1776,11 +1785,11 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t } const uint32_t rhsval = rhs.toUInt(); if (rhsval < static_cast(lhs.width())) { - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { setBit(bit, lhs.bitIsExtend(bit + rhsval, lbits)); } } else { - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { setBit(bit, lhs.bitIs(lbits - 1)); // 0/1/X/Z } } @@ -1797,7 +1806,7 @@ V3Number& V3Number::opShiftL(const V3Number& lhs, const V3Number& rhs) { if (rhs.bitIs1(bit)) return *this; // shift of over 2^32 must be zero } const uint32_t rhsval = rhs.toUInt(); - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (bit >= static_cast(rhsval)) setBit(bit, lhs.bitIs(bit - rhsval)); } return *this; @@ -1825,7 +1834,7 @@ V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) { setZero(); // Addem int carry = 0; - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { const int sum = ((lhs.bitIs1(bit) ? 1 : 0) + (rhs.bitIs1(bit) ? 1 : 0) + carry); if (sum & 1) setBit(bit, 1); carry = (sum >= 2); @@ -1852,15 +1861,15 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) { opCleanThis(); // Mult produces extra bits in result } else { for (int lword = 0; lword < lhs.words(); lword++) { - const uint64_t lwordval = static_cast(lhs.m_value[lword].m_value); + const uint64_t lwordval = static_cast(lhs.m_data.num()[lword].m_value); if (lwordval == 0) continue; for (int rword = 0; rword < rhs.words(); rword++) { - const uint64_t rwordval = static_cast(rhs.m_value[rword].m_value); + const uint64_t rwordval = static_cast(rhs.m_data.num()[rword].m_value); if (rwordval == 0) continue; uint64_t mul = lwordval * rwordval; - for (int qword = lword + rword; qword < this->words(); qword++) { - mul += static_cast(m_value[qword].m_value); - m_value[qword].m_value = (mul & 0xffffffffULL); + for (int qword = lword + rword; qword < words(); qword++) { + mul += static_cast(m_data.num()[qword].m_value); + m_data.num()[qword].m_value = (mul & 0xffffffffULL); mul = (mul >> 32ULL) & 0xffffffffULL; if (mul == 0) break; } @@ -1977,17 +1986,18 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool if (vw == 1) { // Single divisor word breaks rest of algorithm uint64_t k = 0; for (int j = uw - 1; j >= 0; j--) { - const uint64_t unw64 = ((k << 32ULL) + static_cast(lhs.m_value[j].m_value)); - m_value[j].m_value = unw64 / static_cast(rhs.m_value[0].m_value); + const uint64_t unw64 + = ((k << 32ULL) + static_cast(lhs.m_data.num()[j].m_value)); + m_data.num()[j].m_value = unw64 / static_cast(rhs.m_data.num()[0].m_value); k = unw64 - - (static_cast(m_value[j].m_value) - * static_cast(rhs.m_value[0].m_value)); + - (static_cast(m_data.num()[j].m_value) + * static_cast(rhs.m_data.num()[0].m_value)); } UINFO(9, " opmoddiv-1w " << lhs << " " << rhs << " q=" << *this << " rem=0x" << std::hex << k << std::dec << endl); if (is_modulus) { setZero(); - m_value[0].m_value = k; + m_data.num()[0].m_value = k; } opCleanThis(); return *this; @@ -1998,7 +2008,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool uint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized // Zero for ease of debugging and to save having to zero for shifts - for (int i = 0; i < words; i++) { m_value[i].m_value = 0; } + for (int i = 0; i < words; i++) { m_data.num()[i].m_value = 0; } for (int i = 0; i < words + 1; i++) { un[i] = vn[i] = 0; } // +1 as vn may get extra word // Algorithm requires divisor MSB to be set @@ -2006,22 +2016,22 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool const int s = 31 - ((vmsbp1 - 1) & 31); // shift amount (0...31) const uint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value for (int i = vw - 1; i > 0; i--) { - vn[i] = (rhs.m_value[i].m_value << s) - | (shift_mask & (rhs.m_value[i - 1].m_value >> (32 - s))); + vn[i] = (rhs.m_data.num()[i].m_value << s) + | (shift_mask & (rhs.m_data.num()[i - 1].m_value >> (32 - s))); } - vn[0] = rhs.m_value[0].m_value << s; + vn[0] = rhs.m_data.num()[0].m_value << s; // Copy and shift dividend by same amount; may set new upper word if (s) { - un[uw] = lhs.m_value[uw - 1].m_value >> (32 - s); + un[uw] = lhs.m_data.num()[uw - 1].m_value >> (32 - s); } else { un[uw] = 0; } for (int i = uw - 1; i > 0; i--) { - un[i] = (lhs.m_value[i].m_value << s) - | (shift_mask & (lhs.m_value[i - 1].m_value >> (32 - s))); + un[i] = (lhs.m_data.num()[i].m_value << s) + | (shift_mask & (lhs.m_data.num()[i - 1].m_value >> (32 - s))); } - un[0] = lhs.m_value[0].m_value << s; + un[0] = lhs.m_data.num()[0].m_value << s; // printf(" un="); for (int i=5; i>=0; i--) printf(" %08x",un[i]); printf("\n"); // printf(" vn="); for (int i=5; i>=0; i--) printf(" %08x",vn[i]); printf("\n"); @@ -2052,11 +2062,11 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool } t = un[j + vw] - k; un[j + vw] = t; - this->m_value[j].m_value = qhat; // Save quotient digit + m_data.num()[j].m_value = qhat; // Save quotient digit if (t < 0) { // Over subtracted; correct by adding back - this->m_value[j].m_value--; + m_data.num()[j].m_value--; k = 0; for (int i = 0; i < vw; i++) { t = static_cast(un[i + j]) + static_cast(vn[i]) + k; @@ -2074,9 +2084,9 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool if (is_modulus) { // modulus // Need to reverse normalization on copy to output for (int i = 0; i < vw; i++) { - m_value[i].m_value = (un[i] >> s) | (shift_mask & (un[i + 1] << (32 - s))); + m_data.num()[i].m_value = (un[i] >> s) | (shift_mask & (un[i + 1] << (32 - s))); } - for (int i = vw; i < words; i++) m_value[i].m_value = 0; + for (int i = vw; i < words; i++) m_data.num()[i].m_value = 0; opCleanThis(); UINFO(9, " opmoddiv-mod " << lhs << " " << rhs << " now=" << *this << endl); return *this; @@ -2110,7 +2120,7 @@ V3Number& V3Number::opPow(const V3Number& lhs, const V3Number& rhs, bool lsign, } if (lhs.isEqZero()) return setZero(); setZero(); - m_value[0].m_value = 1; + m_data.num()[0].m_value = 1; V3Number power(&lhs, width()); power.opAssign(lhs); for (int bit = 0; bit < rhs.width(); bit++) { @@ -2122,7 +2132,7 @@ V3Number& V3Number::opPow(const V3Number& lhs, const V3Number& rhs, bool lsign, if (rhs.bitIs1(bit)) { // out *= power V3Number lastOut(&lhs, width()); lastOut.opAssign(*this); - this->opMul(lastOut, power); + opMul(lastOut, power); // UINFO(0, "pow "<width(); bit++) { @@ -2198,14 +2215,14 @@ V3Number& V3Number::opClean(const V3Number& lhs, uint32_t bits) { return opSel(l void V3Number::opCleanThis(bool warnOnTruncation) { // Clean MSB of number NUM_ASSERT_LOGIC_ARGS1(*this); - const ValueAndX v = m_value[words() - 1]; + const ValueAndX v = m_data.num()[words() - 1]; const uint32_t newValueMsb = v.m_value & hiWordMask(); const uint32_t newValueXMsb = v.m_valueX & hiWordMask(); if (warnOnTruncation && (newValueMsb != v.m_value || newValueXMsb != v.m_valueX)) { // Displaying in decimal avoids hiWordMask truncation v3warn(WIDTH, "Value too large for " << width() << " bit number: " << displayed("%d")); } - m_value[words() - 1] = {newValueMsb, newValueXMsb}; + m_data.num()[words() - 1] = {newValueMsb, newValueXMsb}; } V3Number& V3Number::opSel(const V3Number& lhs, const V3Number& msb, const V3Number& lsb) { @@ -2221,7 +2238,7 @@ V3Number& V3Number::opSel(const V3Number& lhs, uint32_t msbval, uint32_t lsbval) NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); int ibit = lsbval; - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < width(); bit++) { if (ibit >= 0 && ibit < lhs.width() && ibit <= static_cast(msbval)) { setBit(bit, lhs.bitIs(ibit)); } else { @@ -2317,21 +2334,17 @@ V3Number& V3Number::opRealToBits(const V3Number& lhs) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_DOUBLE_ARGS1(lhs); // Conveniently our internal format is identical so we can copy bits... - if (lhs.width() != 64 || this->width() != 64) { - v3fatalSrc("Real operation on wrong sized number"); - } + if (lhs.width() != 64 || width() != 64) v3fatalSrc("Real operation on wrong sized number"); + m_data.setLogic(); opAssign(lhs); - m_double = false; return *this; } V3Number& V3Number::opBitsToRealD(const V3Number& lhs) { NUM_ASSERT_OP_ARGS1(lhs); // Conveniently our internal format is identical so we can copy bits... - if (lhs.width() != 64 || this->width() != 64) { - v3fatalSrc("Real operation on wrong sized number"); - } + if (lhs.width() != 64 || width() != 64) v3fatalSrc("Real operation on wrong sized number"); + m_data.setDouble(); opAssign(lhs); - m_double = true; return *this; } V3Number& V3Number::opNegateD(const V3Number& lhs) { diff --git a/src/V3Number.h b/src/V3Number.h index fe2f11c5d..1dbec5d36 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -22,8 +22,10 @@ #include "V3Error.h" #include "V3Hash.h" +#include "V3StdFuture.h" #include +#include #include #include #include @@ -42,8 +44,6 @@ class AstNode; class AstNodeDType; class FileLine; -// Holds a few entries of ValueAndX to avoid dynamic allocation in std::vector for less width of -// constants class V3NumberData final { public: // TYPES @@ -57,53 +57,294 @@ public: } }; + enum class V3NumberDataType : uint8_t { + UNINITIALIZED = 0, + LOGIC = 1, + DOUBLE = 2, + STRING = 3, + }; + friend std::ostream& operator<<(std::ostream& os, const V3NumberDataType& rhs) { + switch (rhs) { + case V3NumberDataType::UNINITIALIZED: return os << "UNINITIALIZED"; + case V3NumberDataType::LOGIC: return os << "LOGIC"; + case V3NumberDataType::DOUBLE: return os << "DOUBLE"; + case V3NumberDataType::STRING: return os << "STRING"; + } + return os; + } + private: + // CONSTANTS + // At least 2 words (64 fourstate bits). 4 words (128 fourstate bits) in most cases, + // i.e. when std::string has 32 bytes. + static constexpr int INLINE_WORDS + = vlstd::max(2UL, vlstd::max(sizeof(std::string) / sizeof(ValueAndX), + sizeof(std::vector) / sizeof(ValueAndX))); + // When m_width > MAX_INLINE_WIDTH number is stored in m_dynamicNumber. + // Otherwise number is stored in m_inlineNumber. + static constexpr int MAX_INLINE_WIDTH = INLINE_WORDS * sizeof(ValueAndX) / 2 * 8; + // MEMBERS - static constexpr size_t m_inlinedSize = 2; // Can hold 64 bit without dynamic allocation - ValueAndX m_inlined[m_inlinedSize]; // Holds the beginning m_inlinedSize words - std::vector m_data; // Holds m_inlinedSize-th word and later + union { + std::array m_inlineNumber; + std::vector m_dynamicNumber; + std::string m_string; + }; + + int m_width = 0; // Width (in bits) as specified/calculated + V3NumberDataType m_type; + +public: + bool m_sized : 1; // True if the user specified the width, else we track it. + bool m_signed : 1; // True if signed value + bool m_isNull : 1; // True if "null" versus normal 0 + bool m_fromString : 1; // True if from string literal + bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width public: // CONSTRUCTORS - V3NumberData() { - for (size_t i = 0; i < m_inlinedSize; ++i) m_inlined[i] = {0, 0}; + V3NumberData() + : m_type{V3NumberDataType::UNINITIALIZED} + , m_sized{false} + , m_signed{false} + , m_isNull{false} + , m_fromString{false} + , m_autoExtend{false} {} + + ~V3NumberData() { destroyStoredValue(); } + + V3NumberData(const V3NumberData& other) + : m_width{other.m_width} + , m_type{other.m_type} + , m_sized{other.m_sized} + , m_signed{other.m_signed} + , m_isNull{other.m_isNull} + , m_fromString{other.m_fromString} + , m_autoExtend{other.m_autoExtend} { + if (other.isInlineNumber()) { + initInlineNumber(other.m_inlineNumber); + } else if (other.isDynamicNumber()) { + initDynamicNumber(other.m_dynamicNumber); + } else if (other.isString()) { + initString(other.m_string); + } } + V3NumberData& operator=(const V3NumberData& other) { + if (other.isInlineNumber()) { + destroyStoredValue(); + initInlineNumber(other.m_inlineNumber); + } else if (other.isDynamicNumber()) { + reinitWithOrAssignDynamicNumber(other.m_dynamicNumber); + } else if (other.isString()) { + reinitWithOrAssignString(other.m_string); + } else { + destroyStoredValue(); + } + m_width = other.m_width; + m_type = other.m_type; + m_sized = other.m_sized; + m_signed = other.m_signed; + m_isNull = other.m_isNull; + m_fromString = other.m_fromString; + m_autoExtend = other.m_autoExtend; + return *this; + } + + V3NumberData(V3NumberData&& other) + : m_width{other.m_width} + , m_type{other.m_type} + , m_sized{other.m_sized} + , m_signed{other.m_signed} + , m_isNull{other.m_isNull} + , m_fromString{other.m_fromString} + , m_autoExtend{other.m_autoExtend} { + if (other.isInlineNumber()) { + initInlineNumber(other.m_inlineNumber); + } else if (other.isDynamicNumber()) { + initDynamicNumber(std::move(other.m_dynamicNumber)); + } else if (other.isString()) { + initString(std::move(other.m_string)); + } + other.m_type = V3NumberDataType::UNINITIALIZED; + } + + V3NumberData& operator=(V3NumberData&& other) { + if (other.isInlineNumber()) { + destroyStoredValue(); + initInlineNumber(other.m_inlineNumber); + } else if (other.isDynamicNumber()) { + reinitWithOrAssignDynamicNumber(std::move(other.m_dynamicNumber)); + } else if (other.isString()) { + reinitWithOrAssignString(std::move(other.m_string)); + } else { + destroyStoredValue(); + } + m_width = other.m_width; + m_type = other.m_type; + m_sized = other.m_sized; + m_signed = other.m_signed; + m_isNull = other.m_isNull; + m_fromString = other.m_fromString; + m_autoExtend = other.m_autoExtend; + other.m_type = V3NumberDataType::UNINITIALIZED; + return *this; + } + + // ACCESSORS + ValueAndX* num() { + UASSERT(isNumber(), "`num` member accessed when data type is " << m_type); + return isInlineNumber() ? m_inlineNumber.data() : m_dynamicNumber.data(); + } + const ValueAndX* num() const { + UASSERT(isNumber(), "`num` member accessed when data type is " << m_type); + return isInlineNumber() ? m_inlineNumber.data() : m_dynamicNumber.data(); + } + std::string& str() { + UASSERT(isString(), "`str` member accessed when data type is " << m_type); + return m_string; + } + const std::string& str() const { + UASSERT(isString(), "`str` member accessed when data type is " << m_type); + return m_string; + } + + int width() const { return m_width; } + V3NumberDataType type() const { return m_type; } + // METHODS - void ensureSizeAtLeast(size_t s) { - if (VL_UNLIKELY(s > m_data.size() + m_inlinedSize)) m_data.resize(s - m_inlinedSize); + void resize(int bitsCount) { + if (m_width == bitsCount) return; + if (bitsToWords(m_width) == bitsToWords(bitsCount)) { + m_width = bitsCount; + return; + } + if (isDynamicNumber()) { + if (bitsCount > MAX_INLINE_WIDTH) { + m_dynamicNumber.resize(bitsToWords(bitsCount)); + } else { + const auto dynamicBits = std::move(m_dynamicNumber); + destroyDynamicNumber(); + initInlineNumber(); + std::memcpy(m_inlineNumber.data(), dynamicBits.data(), sizeof(m_inlineNumber)); + } + } else if (isInlineNumber()) { + if (bitsCount > MAX_INLINE_WIDTH) { + const auto bits = m_inlineNumber; + initDynamicNumber(bitsToWords(bitsCount)); + std::memcpy(m_dynamicNumber.data(), bits.data(), sizeof(bits)); + } + } + m_width = bitsCount; } - ValueAndX& operator[](size_t idx) { - UDEBUGONLY(UASSERT(idx < m_data.size() + m_inlinedSize, - "idx:" << idx << " size:" << m_data.size() - << " inlinedSize:" << m_inlinedSize);); - return idx >= m_inlinedSize ? m_data[idx - m_inlinedSize] : m_inlined[idx]; + + void setString() { + // If there has been a string already it is kept intact. + if (isString()) return; + if (isDynamicNumber()) destroyDynamicNumber(); + initString(); + m_type = V3NumberDataType::STRING; + } + void setString(std::string&& s) { + reinitWithOrAssignString(std::move(s)); + m_type = V3NumberDataType::STRING; + } + void setString(const std::string& s) { + reinitWithOrAssignString(s); + m_type = V3NumberDataType::STRING; + } + + void setDouble() { + destroyStoredValue(); + if (!isInlineNumber()) initInlineNumber(); + m_type = V3NumberDataType::DOUBLE; + resize(64); + } + + void setLogic() { + if (isString()) destroyString(); + if (!isNumber()) { + if (m_width <= MAX_INLINE_WIDTH) { + initInlineNumber(); + } else { + initDynamicNumber(bitsToWords(m_width)); + } + } + m_type = V3NumberDataType::LOGIC; + resize(m_width); + } + +private: + static constexpr int bitsToWords(int bitsCount) { return (bitsCount + 31) / 32; } + + bool isNumber() const { + return m_type == V3NumberDataType::DOUBLE || m_type == V3NumberDataType::LOGIC; + } + bool isInlineNumber() const { + return (m_width <= MAX_INLINE_WIDTH) + && (m_type == V3NumberDataType::DOUBLE || m_type == V3NumberDataType::LOGIC); + } + bool isDynamicNumber() const { + return (m_width > MAX_INLINE_WIDTH) && (m_type == V3NumberDataType::LOGIC); + } + bool isString() const { return m_type == V3NumberDataType::STRING; } + + template + void initInlineNumber(Args&&... args) { + new (&m_inlineNumber) std::array(std::forward(args)...); + } + template + void initDynamicNumber(Args&&... args) { + new (&m_dynamicNumber) std::vector(std::forward(args)...); + } + template + void initString(Args&&... args) { + new (&m_string) std::string(std::forward(args)...); + } + + void destroyDynamicNumber() { m_dynamicNumber.~vector(); } + void destroyString() { m_string.~string(); } + void destroyStoredValue() { + if (isString()) + destroyString(); + else if (isDynamicNumber()) + destroyDynamicNumber(); + } + + template + void reinitWithOrAssignDynamicNumber(T&& s) { + if (isDynamicNumber()) { + m_dynamicNumber = std::forward(s); + return; + } + if (isString()) destroyString(); + initDynamicNumber(std::forward(s)); + } + template + void reinitWithOrAssignString(T&& s) { + if (isString()) { + m_string = std::forward(s); + return; + } + if (isDynamicNumber()) destroyDynamicNumber(); + initString(std::forward(s)); } - const ValueAndX& operator[](size_t idx) const { return const_cast(*this)[idx]; } }; class V3Number final { // TYPES using ValueAndX = V3NumberData::ValueAndX; + using V3NumberDataType = V3NumberData::V3NumberDataType; + + // MEMBERS + V3NumberData m_data; + AstNode* m_nodep = nullptr; // Parent node + FileLine* m_fileline = nullptr; - // Large 4-state number handling - int m_width; // Width as specified/calculated. - bool m_sized : 1; // True if the user specified the width, else we track it. - bool m_signed : 1; // True if signed value - bool m_double : 1; // True if double real value - bool m_isNull : 1; // True if "null" versus normal 0 - bool m_isString : 1; // True if string - bool m_fromString : 1; // True if from string literal - bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width - FileLine* m_fileline; - AstNode* m_nodep; // Parent node - V3NumberData m_value; // Value and X/Z information - string m_stringVal; // If isString, the value of the string // METHODS V3Number& setSingleBits(char value); V3Number& setString(const string& str) { - m_isString = true; - m_stringVal = str; + m_data.setString(str); return *this; } void opCleanThis(bool warnOnTruncation = false); @@ -116,10 +357,10 @@ public: V3Number& setLong(uint32_t value); V3Number& setLongS(int32_t value); V3Number& setDouble(double value); - void setBit(int bit, char value) { // Note must be pre-zeroed! - if (bit >= m_width) return; + void setBit(int bit, char value) { // Note: must be initialized as number and pre-zeroed! + if (bit >= m_data.width()) return; const uint32_t mask = (1UL << (bit & 31)); - ValueAndX& v = m_value[bit / 32]; + ValueAndX& v = m_data.num()[bit / 32]; if (value == '0' || value == 0) { v.m_value &= ~mask; v.m_valueX &= ~mask; @@ -137,65 +378,71 @@ public: private: char bitIs(int bit) const { - if (bit >= m_width || bit < 0) { + if (bit >= m_data.width() || bit < 0) { // We never sign extend return '0'; } - const ValueAndX v = m_value[bit / 32]; + const ValueAndX v = m_data.num()[bit / 32]; return ("01zx"[(((v.m_value & (1UL << (bit & 31))) ? 1 : 0) | ((v.m_valueX & (1UL << (bit & 31))) ? 2 : 0))]); } char bitIsExtend(int bit, int lbits) const { // lbits usually = width, but for C optimizations width=32_bits, lbits = 32_or_less if (bit < 0) return '0'; - UASSERT(lbits <= m_width, "Extend of wrong size"); + UASSERT(lbits <= m_data.width(), "Extend of wrong size"); if (bit >= lbits) { bit = lbits ? lbits - 1 : 0; - const ValueAndX v = m_value[bit / 32]; + const ValueAndX v = m_data.num()[bit / 32]; // We do sign extend return ("01zx"[(((v.m_value & (1UL << (bit & 31))) ? 1 : 0) | ((v.m_valueX & (1UL << (bit & 31))) ? 2 : 0))]); } - const ValueAndX v = m_value[bit / 32]; + const ValueAndX v = m_data.num()[bit / 32]; return ("01zx"[(((v.m_value & (1UL << (bit & 31))) ? 1 : 0) | ((v.m_valueX & (1UL << (bit & 31))) ? 2 : 0))]); } public: bool bitIs0(int bit) const { + if (!isNumber()) return false; if (bit < 0) return false; - if (bit >= m_width) return !bitIsXZ(m_width - 1); - const ValueAndX v = m_value[bit / 32]; + if (bit >= m_data.width()) return !bitIsXZ(m_data.width() - 1); + const ValueAndX v = m_data.num()[bit / 32]; return ((v.m_value & (1UL << (bit & 31))) == 0 && !(v.m_valueX & (1UL << (bit & 31)))); } bool bitIs1(int bit) const { + if (!isNumber()) return false; if (bit < 0) return false; - if (bit >= m_width) return false; - const ValueAndX v = m_value[bit / 32]; + if (bit >= m_data.width()) return false; + const ValueAndX v = m_data.num()[bit / 32]; return ((v.m_value & (1UL << (bit & 31))) && !(v.m_valueX & (1UL << (bit & 31)))); } bool bitIs1Extend(int bit) const { + if (!isNumber()) return false; if (bit < 0) return false; - if (bit >= m_width) return bitIs1Extend(m_width - 1); - const ValueAndX v = m_value[bit / 32]; + if (bit >= m_data.width()) return bitIs1Extend(m_data.width() - 1); + const ValueAndX v = m_data.num()[bit / 32]; return ((v.m_value & (1UL << (bit & 31))) && !(v.m_valueX & (1UL << (bit & 31)))); } bool bitIsX(int bit) const { + if (!isNumber()) return false; if (bit < 0) return false; - if (bit >= m_width) return bitIsZ(m_width - 1); - const ValueAndX v = m_value[bit / 32]; + if (bit >= m_data.width()) return bitIsZ(m_data.width() - 1); + const ValueAndX v = m_data.num()[bit / 32]; return ((v.m_value & (1UL << (bit & 31))) && (v.m_valueX & (1UL << (bit & 31)))); } bool bitIsXZ(int bit) const { + if (!isNumber()) return false; if (bit < 0) return false; - if (bit >= m_width) return bitIsXZ(m_width - 1); - const ValueAndX v = m_value[bit / 32]; + if (bit >= m_data.width()) return bitIsXZ(m_data.width() - 1); + const ValueAndX v = m_data.num()[bit / 32]; return ((v.m_valueX & (1UL << (bit & 31)))); } bool bitIsZ(int bit) const { + if (!isNumber()) return false; if (bit < 0) return false; - if (bit >= m_width) return bitIsZ(m_width - 1); - const ValueAndX v = m_value[bit / 32]; + if (bit >= m_data.width()) return bitIsZ(m_data.width() - 1); + const ValueAndX v = m_data.num()[bit / 32]; return ((~v.m_value & (1UL << (bit & 31))) && (v.m_valueX & (1UL << (bit & 31)))); } @@ -217,10 +464,12 @@ private: public: // CONSTRUCTORS explicit V3Number(AstNode* nodep) { init(nodep, 1); } - V3Number(AstNode* nodep, int width) { init(nodep, width); } // 0=unsized + V3Number(AstNode* nodep, int width) { // 0=unsized + init(nodep, width, width > 0); + } V3Number(AstNode* nodep, int width, uint32_t value, bool sized = true) { init(nodep, width, sized); - m_value[0].m_value = value; + m_data.num()[0].m_value = value; opCleanThis(); } // Create from a verilog 32'hxxxx number. @@ -233,15 +482,16 @@ public: V3Number(VerilogStringLiteral, AstNode* nodep, const string& str); class String {}; V3Number(String, AstNode* nodep, const string& value) { - init(nodep, 0); + init(nodep); setString(value); - m_fromString = true; + m_data.m_fromString = true; } class Null {}; V3Number(Null, AstNode* nodep) { - init(nodep, 0); - m_isNull = true; - m_autoExtend = true; + init(nodep); + m_data.setLogic(); + m_data.m_isNull = true; + m_data.m_autoExtend = true; } explicit V3Number(const V3Number* nump, int width = 1) { init(nullptr, width); @@ -249,7 +499,7 @@ public: } V3Number(const V3Number* nump, int width, uint32_t value) { init(nullptr, width); - m_value[0].m_value = value; + m_data.num()[0].m_value = value; opCleanThis(); m_fileline = nump->fileline(); } @@ -259,19 +509,31 @@ public: } V3Number(AstNode* nodep, const AstNodeDType* nodedtypep); + V3Number(const V3Number& other) = default; + V3Number& operator=(const V3Number& other) = default; + + V3Number(V3Number&& other) = default; + V3Number& operator=(V3Number&& other) = default; + + ~V3Number() {} + private: void V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl); - void init(AstNode* nodep, int swidth, bool sized = true) { + void init(AstNode* nodep, int swidth = -1, bool sized = true) { setNames(nodep); - // dtype info does NOT from nodep's dtype; nodep only for error reporting - m_signed = false; - m_double = false; - m_isNull = false; - m_isString = false; - m_autoExtend = false; - m_fromString = false; - width(swidth, sized); - for (int i = 0; i < words(); ++i) m_value[i] = {0, 0}; + if (swidth >= 0) { + if (swidth == 0) { + swidth = 1; + sized = false; + } + m_data.setLogic(); + m_data.resize(swidth); + m_data.m_sized = sized; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0}; + } else { + m_data.resize(1); + m_data.m_sized = false; + } } void setNames(AstNode* nodep); static string displayPad(size_t fmtsize, char pad, bool left, const string& in); @@ -282,15 +544,8 @@ public: void v3errorEnd(std::ostringstream& sstr) const; void v3errorEndFatal(std::ostringstream& sstr) const VL_ATTR_NORETURN; void width(int width, bool sized = true) { - // Set width. Only set m_width here, as we need to tweak vector size - if (width) { - m_sized = sized; - m_width = width; - } else { - m_sized = false; - m_width = 1; - } - m_value.ensureSizeAtLeast(words()); + m_data.m_sized = sized; + m_data.resize(width); } // SETTERS @@ -305,25 +560,39 @@ public: string ascii(bool prefixed = true, bool cleanVerilog = false) const; string displayed(AstNode* nodep, const string& vformat) const; static bool displayedFmtLegal(char format, bool isScan); // Is this a valid format letter? - int width() const { return m_width; } + int width() const { return m_data.width(); } int widthMin() const; // Minimum width that can represent this number (~== log2(num)+1) - bool sized() const { return m_sized; } - bool autoExtend() const { return m_autoExtend; } - bool isFromString() const { return m_fromString; } + bool sized() const { return m_data.m_sized; } + bool autoExtend() const { return m_data.m_autoExtend; } + bool isFromString() const { return m_data.m_fromString; } + V3NumberDataType dataType() const { return m_data.type(); } + void dataType(V3NumberDataType newType) { + if (dataType() == newType) return; + UASSERT(newType != V3NumberDataType::UNINITIALIZED, "Can't set type to UNINITIALIZED."); + switch (newType) { + case V3NumberDataType::STRING: m_data.setString(); break; + case V3NumberDataType::DOUBLE: m_data.setDouble(); break; + case V3NumberDataType::LOGIC: m_data.setLogic(); break; + case V3NumberDataType::UNINITIALIZED: break; + } + } // Only correct for parsing of numbers from strings, otherwise not used // (use AstConst::isSigned()) - bool isSigned() const { return m_signed; } - // Only correct for parsing of numbers from strings, otherwise not used - // (use AstConst::isSigned()) - bool isDouble() const { return m_double; } - // Only if have 64 bit value loaded, and want to indicate it's real - bool isString() const { return m_isString; } - bool isNegative() const { return bitIs1(width() - 1); } - bool isNull() const { return m_isNull; } + bool isSigned() const { return m_data.m_signed; } + void isSigned(bool ssigned) { m_data.m_signed = ssigned; } + bool isDouble() const { return dataType() == V3NumberDataType::DOUBLE; } + bool isString() const { return dataType() == V3NumberDataType::STRING; } + bool isNumber() const { + return m_data.type() == V3NumberDataType::LOGIC + || m_data.type() == V3NumberDataType::DOUBLE; + } + bool isNegative() const { return !isString() && bitIs1(width() - 1); } + bool isNull() const { return m_data.m_isNull; } bool isFourState() const; bool hasZ() const { + if (isString()) return false; for (int i = 0; i < words(); i++) { - const ValueAndX v = m_value[i]; + const ValueAndX v = m_data.num()[i]; if ((~v.m_value) & v.m_valueX) return true; } return false; @@ -337,11 +606,10 @@ public: bool isEqAllOnes(int optwidth = 0) const; bool isCaseEq(const V3Number& rhs) const; // operator== bool isLtXZ(const V3Number& rhs) const; // operator< with XZ compared - void isSigned(bool ssigned) { m_signed = ssigned; } bool isAnyX() const; bool isAnyXZ() const; bool isAnyZ() const; - bool isMsbXZ() const { return bitIsXZ(m_width - 1); } + bool isMsbXZ() const { return bitIsXZ(m_data.width() - 1); } uint32_t toUInt() const; int32_t toSInt() const; uint64_t toUQuad() const; diff --git a/src/V3StdFuture.h b/src/V3StdFuture.h new file mode 100644 index 000000000..32a4f7539 --- /dev/null +++ b/src/V3StdFuture.h @@ -0,0 +1,30 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Backports of features from future C++ versions +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2022 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 +// +//************************************************************************* + +#ifndef VERILATOR_V3STDFUTURE_H_ +#define VERILATOR_V3STDFUTURE_H_ + +namespace vlstd { + +// constexpr std::max with arguments passed by value (required by constexpr before C++14) +template +constexpr T max(T a, T b) { + return a > b ? a : b; +} + +}; // namespace vlstd + +#endif // Guard