From 22e0f3edbea47bd88dca78a85fd5180b104f7f35 Mon Sep 17 00:00:00 2001 From: Yutetsu TAKATSUKASA Date: Wed, 30 Jun 2021 21:20:56 +0900 Subject: [PATCH] Introduce small object optimization to V3Number (#3034) --- src/V3Number.cpp | 173 ++++++++++++++++++++++++----------------------- src/V3Number.h | 116 +++++++++++++++++++++---------- 2 files changed, 169 insertions(+), 120 deletions(-) diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 13e60f862..fe0197718 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -101,10 +101,9 @@ V3Number::V3Number(VerilogStringLiteral, AstNode* nodep, const string& str) { m_fromString = true; for (unsigned pos = 0; pos < str.length(); ++pos) { const int topos = str.length() - 1 - pos; + ValueAndX& v = m_value[topos / 4]; for (int bit = 0; bit < 8; ++bit) { - if (str[pos] & (1UL << bit)) { - m_value[topos / 4] |= (1UL << (bit + (topos % 4) * 8)); - } + if (str[pos] & (1UL << bit)) { v.m_value |= (1UL << (bit + (topos % 4) * 8)); } } } opCleanThis(true); @@ -159,7 +158,7 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl) base = 'd'; } - for (int i = 0; i < words(); ++i) m_value[i] = m_valueX[i] = 0; + for (int i = 0; i < words(); ++i) m_value[i] = {0, 0}; // Special SystemVerilog unsized constructs if (base == '0') { @@ -216,7 +215,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] = val; + m_value[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 @@ -373,31 +372,31 @@ int V3Number::log2b(uint32_t num) { // Setters V3Number& V3Number::setZero() { - for (int i = 0; i < words(); i++) m_value[i] = m_valueX[i] = 0; + for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; return *this; } V3Number& V3Number::setQuad(vluint64_t value) { - for (int i = 0; i < words(); i++) m_value[i] = m_valueX[i] = 0; - m_value[0] = value & 0xffffffffULL; - m_value[1] = (value >> 32ULL) & 0xffffffffULL; + 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; opCleanThis(); return *this; } V3Number& V3Number::setLong(uint32_t value) { - for (int i = 0; i < words(); i++) m_value[i] = m_valueX[i] = 0; - m_value[0] = value; + for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; + m_value[0].m_value = value; opCleanThis(); return *this; } V3Number& V3Number::setLongS(vlsint32_t value) { - for (int i = 0; i < words(); i++) m_value[i] = m_valueX[i] = 0; + for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; union { uint32_t u; vlsint32_t s; } u; u.s = value; if (u.s) {} - m_value[0] = u.u; + m_value[0].m_value = u.u; opCleanThis(); return *this; } @@ -410,41 +409,35 @@ V3Number& V3Number::setDouble(double value) { } u; u.d = value; if (u.d != 0.0) {} - for (int i = 2; i < words(); i++) m_value[i] = m_valueX[i] = 0; - m_value[0] = u.u[0]; - m_value[1] = u.u[1]; + 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]; return *this; } V3Number& V3Number::setSingleBits(char value) { - for (int i = 1 /*upper*/; i < words(); i++) m_value[i] = m_valueX[i] = 0; - m_value[0] = (value == '1' || value == 'x' || value == 1 || value == 3); - m_valueX[0] = (value == 'z' || value == 'x' || value == 2 || value == 3); + 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)}; return *this; } V3Number& V3Number::setAllBits0() { - for (int i = 0; i < words(); i++) { m_value[i] = m_valueX[i] = 0; } + for (int i = 0; i < words(); i++) { m_value[i] = {0, 0}; } return *this; } V3Number& V3Number::setAllBits1() { - for (int i = 0; i < words(); i++) { - m_value[i] = ~0; - m_valueX[i] = 0; - } + for (int i = 0; i < words(); i++) { m_value[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] = m_valueX[i] = ~0; } + for (int i = 0; i < words(); i++) { m_value[i] = {~0u, ~0u}; } opCleanThis(); return *this; } V3Number& V3Number::setAllBitsZ() { - for (int i = 0; i < words(); i++) { - m_value[i] = 0; - m_valueX[i] = ~0; - } + for (int i = 0; i < words(); i++) { m_value[i] = {0, ~0u}; } opCleanThis(); return *this; } @@ -485,7 +478,8 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const { } else if (isString()) { return '"' + toString() + '"'; } else { - if (VL_UNCOVERABLE((m_value[words() - 1] | m_valueX[words() - 1]) & ~hiWordMask())) { + if (VL_UNCOVERABLE((m_value[words() - 1].m_value | m_value[words() - 1].m_valueX) + & ~hiWordMask())) { out << "%E-hidden-bits"; // LCOV_EXCL_LINE } } @@ -704,23 +698,25 @@ 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++) { - str += static_cast((m_value[i] >> 0) & 0xff); - str += static_cast((m_value[i] >> 8) & 0xff); - str += static_cast((m_value[i] >> 16) & 0xff); - str += static_cast((m_value[i] >> 24) & 0xff); + const uint32_t v = m_value[i].m_value; + str += static_cast((v >> 0) & 0xff); + str += static_cast((v >> 8) & 0xff); + str += static_cast((v >> 16) & 0xff); + str += static_cast((v >> 24) & 0xff); } return str; } case 'z': { // Packed 4-state for (int i = 0; i < words(); i++) { - str += static_cast((m_value[i] >> 0) & 0xff); - str += static_cast((m_value[i] >> 8) & 0xff); - str += static_cast((m_value[i] >> 16) & 0xff); - str += static_cast((m_value[i] >> 24) & 0xff); - str += static_cast((m_valueX[i] >> 0) & 0xff); - str += static_cast((m_valueX[i] >> 8) & 0xff); - str += static_cast((m_valueX[i] >> 16) & 0xff); - str += static_cast((m_valueX[i] >> 24) & 0xff); + const ValueAndX v = m_value[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); + str += static_cast((v.m_value >> 24) & 0xff); + str += static_cast((v.m_valueX >> 0) & 0xff); + str += static_cast((v.m_valueX >> 8) & 0xff); + str += static_cast((v.m_valueX >> 16) & 0xff); + str += static_cast((v.m_valueX >> 24) & 0xff); } return str; } @@ -809,12 +805,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]) { + if (m_value[i].m_value) { v3error("Value too wide for 32-bits expected in this context " << *this); break; } } - return m_value[0]; + return m_value[0].m_value; } double V3Number::toDouble() const { @@ -825,8 +821,8 @@ double V3Number::toDouble() const { double d; uint32_t u[2]; } u; - u.u[0] = m_value[0]; - u.u[1] = m_value[1]; + u.u[0] = m_value[0].m_value; + u.u[1] = m_value[1].m_value; return u.d; } @@ -848,14 +844,14 @@ vluint64_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]) { + if (m_value[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]) << 32ULL) - | (static_cast(m_value[0]))); + return ((static_cast(m_value[1].m_value) << 32ULL) + | (static_cast(m_value[0].m_value))); } vlsint64_t V3Number::toSQuad() const { @@ -886,13 +882,13 @@ string V3Number::toString() const { V3Hash V3Number::toHash() const { V3Hash hash(m_width); - for (int i = 0; i < words(); ++i) { hash += m_value[i]; } + for (int i = 0; i < words(); ++i) { hash += m_value[i].m_value; } return hash; } uint32_t V3Number::edataWord(int eword) const { UASSERT(!isFourState(), "edataWord with 4-state " << *this); - return m_value[eword]; + return m_value[eword].m_value; } uint8_t V3Number::dataByte(int byte) const { @@ -901,13 +897,15 @@ uint8_t V3Number::dataByte(int byte) const { bool V3Number::isEqZero() const { for (int i = 0; i < words(); i++) { - if (m_value[i] || m_valueX[i]) return false; + const ValueAndX v = m_value[i]; + if (v.m_value || v.m_valueX) return false; } return true; } bool V3Number::isNeqZero() const { for (int i = 0; i < words(); i++) { - if (m_value[i] & ~m_valueX[i]) return true; + const ValueAndX v = m_value[i]; + if (v.m_value & ~v.m_valueX) return true; } return false; } @@ -918,9 +916,10 @@ bool V3Number::isBitsZero(int msb, int lsb) const { return true; } bool V3Number::isEqOne() const { - if (m_value[0] != 1 || m_valueX[0]) return false; + if (m_value[0].m_value != 1 || m_value[0].m_valueX) return false; for (int i = 1; i < words(); i++) { - if (m_value[i] || m_valueX[i]) return false; + const ValueAndX v = m_value[i]; + if (v.m_value || v.m_valueX) return false; } return true; } @@ -934,7 +933,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_valueX[i]) return true; + if (m_value[i].m_valueX) return true; } return false; } @@ -1129,7 +1128,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] = expr.countBits(ctrl1, ctrl2, ctrl3); + m_value[0].m_value = expr.countBits(ctrl1, ctrl2, ctrl3); opCleanThis(); return *this; } @@ -1138,7 +1137,7 @@ V3Number& V3Number::opCountOnes(const V3Number& lhs) { NUM_ASSERT_LOGIC_ARGS1(lhs); if (lhs.isFourState()) return setAllBitsX(); setZero(); - m_value[0] = lhs.countOnes(); + m_value[0].m_value = lhs.countOnes(); opCleanThis(); return *this; } @@ -1531,8 +1530,10 @@ bool V3Number::isCaseEq(const V3Number& rhs) const { if (isString()) return toString() == rhs.toString(); if (isDouble()) return toDouble() == rhs.toDouble(); if (this->width() != rhs.width()) return false; - if (m_value != rhs.m_value) return false; - return m_valueX == rhs.m_valueX; + for (int i = 0; i < words(); ++i) { + if (!(m_value[i] == rhs.m_value[i])) return false; + } + return true; } V3Number& V3Number::opCaseEq(const V3Number& lhs, const V3Number& rhs) { @@ -1761,15 +1762,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 vluint64_t lwordval = static_cast(lhs.m_value[lword]); + const vluint64_t lwordval = static_cast(lhs.m_value[lword].m_value); if (lwordval == 0) continue; for (int rword = 0; rword < rhs.words(); rword++) { - const vluint64_t rwordval = static_cast(rhs.m_value[rword]); + const vluint64_t rwordval = static_cast(rhs.m_value[rword].m_value); if (rwordval == 0) continue; vluint64_t mul = lwordval * rwordval; for (int qword = lword + rword; qword < this->words(); qword++) { - mul += static_cast(m_value[qword]); - m_value[qword] = (mul & 0xffffffffULL); + mul += static_cast(m_value[qword].m_value); + m_value[qword].m_value = (mul & 0xffffffffULL); mul = (mul >> 32ULL) & 0xffffffffULL; if (mul == 0) break; } @@ -1886,16 +1887,17 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool if (vw == 1) { // Single divisor word breaks rest of algorithm vluint64_t k = 0; for (int j = uw - 1; j >= 0; j--) { - vluint64_t unw64 = ((k << 32ULL) + static_cast(lhs.m_value[j])); - m_value[j] = unw64 / static_cast(rhs.m_value[0]); + vluint64_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); k = unw64 - - (static_cast(m_value[j]) * static_cast(rhs.m_value[0])); + - (static_cast(m_value[j].m_value) + * static_cast(rhs.m_value[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] = k; + m_value[0].m_value = k; } opCleanThis(); return *this; @@ -1906,7 +1908,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] = 0; } + for (int i = 0; i < words; i++) { m_value[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 @@ -1914,20 +1916,22 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool int s = 31 - ((vmsbp1 - 1) & 31); // shift amount (0...31) 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] << s) | (shift_mask & (rhs.m_value[i - 1] >> (32 - s))); + vn[i] = (rhs.m_value[i].m_value << s) + | (shift_mask & (rhs.m_value[i - 1].m_value >> (32 - s))); } - vn[0] = rhs.m_value[0] << s; + vn[0] = rhs.m_value[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] >> (32 - s); + un[uw] = lhs.m_value[uw - 1].m_value >> (32 - s); } else { un[uw] = 0; } for (int i = uw - 1; i > 0; i--) { - un[i] = (lhs.m_value[i] << s) | (shift_mask & (lhs.m_value[i - 1] >> (32 - s))); + un[i] = (lhs.m_value[i].m_value << s) + | (shift_mask & (lhs.m_value[i - 1].m_value >> (32 - s))); } - un[0] = lhs.m_value[0] << s; + un[0] = lhs.m_value[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"); @@ -1958,11 +1962,11 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool } t = un[j + vw] - k; un[j + vw] = t; - this->m_value[j] = qhat; // Save quotient digit + this->m_value[j].m_value = qhat; // Save quotient digit if (t < 0) { // Over subtracted; correct by adding back - this->m_value[j]--; + this->m_value[j].m_value--; k = 0; for (int i = 0; i < vw; i++) { t = static_cast(un[i + j]) + static_cast(vn[i]) + k; @@ -1980,9 +1984,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] = (un[i] >> s) | (shift_mask & (un[i + 1] << (32 - s))); + m_value[i].m_value = (un[i] >> s) | (shift_mask & (un[i + 1] << (32 - s))); } - for (int i = vw; i < words; i++) m_value[i] = 0; + for (int i = vw; i < words; i++) m_value[i].m_value = 0; opCleanThis(); UINFO(9, " opmoddiv-mod " << lhs << " " << rhs << " now=" << *this << endl); return *this; @@ -2016,7 +2020,7 @@ V3Number& V3Number::opPow(const V3Number& lhs, const V3Number& rhs, bool lsign, } if (lhs.isEqZero()) return setZero(); setZero(); - m_value[0] = 1; + m_value[0].m_value = 1; V3Number power(&lhs, width()); power.opAssign(lhs); for (int bit = 0; bit < rhs.width(); bit++) { @@ -2104,15 +2108,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); - uint32_t newValueMsb = m_value[words() - 1] & hiWordMask(); - uint32_t newValueXMsb = m_valueX[words() - 1] & hiWordMask(); - if (warnOnTruncation - && (newValueMsb != m_value[words() - 1] || newValueXMsb != m_valueX[words() - 1])) { + const ValueAndX v = m_value[words() - 1]; + uint32_t newValueMsb = v.m_value & hiWordMask(); + 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; - m_valueX[words() - 1] = newValueXMsb; + m_value[words() - 1] = {newValueMsb, newValueXMsb}; } V3Number& V3Number::opSel(const V3Number& lhs, const V3Number& msb, const V3Number& lsb) { diff --git a/src/V3Number.h b/src/V3Number.h index fd0f41f93..961b9c414 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -38,8 +38,52 @@ inline bool v3EpsilonEqual(double a, double b) { //============================================================================ class AstNode; +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 + struct ValueAndX final { + // Value, with bit 0 in bit 0 of this vector (unless X/Z) + uint32_t m_value; + // Each bit is true if it's X or Z, 10=z, 11=x + uint32_t m_valueX; + bool operator==(const ValueAndX& other) const { + return m_value == other.m_value && m_valueX == other.m_valueX; + } + }; + +private: + // 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 + +public: + // CONSTRUCTORS + V3NumberData() { + for (size_t i = 0; i < m_inlinedSize; ++i) m_inlined[i] = {0, 0}; + } + + // METHODS + void ensureSizeAtLeast(size_t s) { + if (VL_UNLIKELY(s > m_data.size() + m_inlinedSize)) m_data.resize(s - m_inlinedSize); + } + 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]; + } + const ValueAndX& operator[](size_t idx) const { return const_cast(*this)[idx]; } +}; class V3Number final { + // TYPES + using ValueAndX = V3NumberData::ValueAndX; + // 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. @@ -51,8 +95,7 @@ class V3Number final { bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width FileLine* m_fileline; AstNode* m_nodep; // Parent node - std::vector m_value; // Value, with bit 0 in bit 0 of this vector (unless X/Z) - std::vector m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x + V3NumberData m_value; // Value and X/Z information string m_stringVal; // If isString, the value of the string // METHODS V3Number& setSingleBits(char value); @@ -74,18 +117,19 @@ public: void setBit(int bit, char value) { // Note must be pre-zeroed! if (bit >= m_width) return; uint32_t mask = (1UL << (bit & 31)); + ValueAndX& v = m_value[bit / 32]; if (value == '0' || value == 0) { - m_value[bit / 32] &= ~mask; - m_valueX[bit / 32] &= ~mask; + v.m_value &= ~mask; + v.m_valueX &= ~mask; } else if (value == '1' || value == 1) { - m_value[bit / 32] |= mask; - m_valueX[bit / 32] &= ~mask; + v.m_value |= mask; + v.m_valueX &= ~mask; } else if (value == 'z' || value == 2) { - m_value[bit / 32] &= ~mask; - m_valueX[bit / 32] |= mask; + v.m_value &= ~mask; + v.m_valueX |= mask; } else { // X - m_value[bit / 32] |= mask; - m_valueX[bit / 32] |= mask; + v.m_value |= mask; + v.m_valueX |= mask; } } @@ -95,8 +139,9 @@ private: // We never sign extend return '0'; } - return ("01zx"[(((m_value[bit / 32] & (1UL << (bit & 31))) ? 1 : 0) - | ((m_valueX[bit / 32] & (1UL << (bit & 31))) ? 2 : 0))]); + const ValueAndX v = m_value[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 @@ -104,49 +149,52 @@ private: UASSERT(lbits <= m_width, "Extend of wrong size"); if (bit >= lbits) { bit = lbits ? lbits - 1 : 0; + const ValueAndX v = m_value[bit / 32]; // We do sign extend - return ("01zx"[(((m_value[bit / 32] & (1UL << (bit & 31))) ? 1 : 0) - | ((m_valueX[bit / 32] & (1UL << (bit & 31))) ? 2 : 0))]); + return ("01zx"[(((v.m_value & (1UL << (bit & 31))) ? 1 : 0) + | ((v.m_valueX & (1UL << (bit & 31))) ? 2 : 0))]); } - return ("01zx"[(((m_value[bit / 32] & (1UL << (bit & 31))) ? 1 : 0) - | ((m_valueX[bit / 32] & (1UL << (bit & 31))) ? 2 : 0))]); + const ValueAndX v = m_value[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 (bit < 0) return false; if (bit >= m_width) return !bitIsXZ(m_width - 1); - return ((m_value[bit / 32] & (1UL << (bit & 31))) == 0 - && !(m_valueX[bit / 32] & (1UL << (bit & 31)))); + const ValueAndX v = m_value[bit / 32]; + return ((v.m_value & (1UL << (bit & 31))) == 0 && !(v.m_valueX & (1UL << (bit & 31)))); } bool bitIs1(int bit) const { if (bit < 0) return false; if (bit >= m_width) return false; - return ((m_value[bit / 32] & (1UL << (bit & 31))) - && !(m_valueX[bit / 32] & (1UL << (bit & 31)))); + const ValueAndX v = m_value[bit / 32]; + return ((v.m_value & (1UL << (bit & 31))) && !(v.m_valueX & (1UL << (bit & 31)))); } bool bitIs1Extend(int bit) const { if (bit < 0) return false; if (bit >= m_width) return bitIs1Extend(m_width - 1); - return ((m_value[bit / 32] & (1UL << (bit & 31))) - && !(m_valueX[bit / 32] & (1UL << (bit & 31)))); + const ValueAndX v = m_value[bit / 32]; + return ((v.m_value & (1UL << (bit & 31))) && !(v.m_valueX & (1UL << (bit & 31)))); } bool bitIsX(int bit) const { if (bit < 0) return false; if (bit >= m_width) return bitIsZ(m_width - 1); - return ((m_value[bit / 32] & (1UL << (bit & 31))) - && (m_valueX[bit / 32] & (1UL << (bit & 31)))); + const ValueAndX v = m_value[bit / 32]; + return ((v.m_value & (1UL << (bit & 31))) && (v.m_valueX & (1UL << (bit & 31)))); } bool bitIsXZ(int bit) const { if (bit < 0) return false; if (bit >= m_width) return bitIsXZ(m_width - 1); - return ((m_valueX[bit / 32] & (1UL << (bit & 31)))); + const ValueAndX v = m_value[bit / 32]; + return ((v.m_valueX & (1UL << (bit & 31)))); } bool bitIsZ(int bit) const { if (bit < 0) return false; if (bit >= m_width) return bitIsZ(m_width - 1); - return ((~m_value[bit / 32] & (1UL << (bit & 31))) - && (m_valueX[bit / 32] & (1UL << (bit & 31)))); + const ValueAndX v = m_value[bit / 32]; + return ((~v.m_value & (1UL << (bit & 31))) && (v.m_valueX & (1UL << (bit & 31)))); } private: @@ -167,7 +215,7 @@ public: V3Number(AstNode* nodep, int width) { init(nodep, width); } // 0=unsized V3Number(AstNode* nodep, int width, uint32_t value, bool sized = true) { init(nodep, width, sized); - m_value[0] = value; + m_value[0].m_value = value; opCleanThis(); } // Create from a verilog 32'hxxxx number. @@ -195,7 +243,7 @@ public: } V3Number(const V3Number* nump, int width, uint32_t value) { init(nullptr, width); - m_value[0] = value; + m_value[0].m_value = value; opCleanThis(); m_fileline = nump->fileline(); } @@ -212,7 +260,7 @@ private: m_autoExtend = false; m_fromString = false; width(swidth, sized); - for (int i = 0; i < words(); ++i) m_value[i] = m_valueX[i] = 0; + for (int i = 0; i < words(); ++i) m_value[i] = {0, 0}; } void setNames(AstNode* nodep); static string displayPad(size_t fmtsize, char pad, bool left, const string& in); @@ -231,10 +279,7 @@ public: m_sized = false; m_width = 1; } - if (VL_UNLIKELY(m_value.size() < (unsigned)(words() + 1))) { - m_value.resize(words() + 1); - m_valueX.resize(words() + 1); - } + m_value.ensureSizeAtLeast(words()); } // SETTERS @@ -269,7 +314,8 @@ public: bool isFourState() const; bool hasZ() const { for (int i = 0; i < words(); i++) { - if ((~m_value[i]) & m_valueX[i]) return true; + const ValueAndX v = m_value[i]; + if ((~v.m_value) & v.m_valueX) return true; } return false; }