Internals: Minor cleanup of case statement lowering

- Factor out match mask construction
- Rename V3Number method to reflect behaviour
This commit is contained in:
Geza Lore 2026-06-16 14:55:24 +01:00
parent b581f73843
commit f11c4084d1
5 changed files with 32 additions and 29 deletions

View File

@ -174,6 +174,21 @@ class CaseVisitor final : public VNVisitor {
return constp->num().isFourState();
}
// Returns the matching mask and bits for a case item constant condition
// TODO: with 'neverItem' checked, this is alway the same for all types of cases
// 4-state will have to fix this up
static std::pair<V3Number, V3Number> matchPattern(const AstCase* /*casep*/,
const AstConst* condp) {
// As one to encourage NRVO/move
std::pair<V3Number, V3Number> pairMaskBits{
std::piecewise_construct, //
std::forward_as_tuple(condp->fileline(), condp->width(), 0),
std::forward_as_tuple(condp->fileline(), condp->width(), 0)};
pairMaskBits.first.opBitsNonXZ(condp->num());
pairMaskBits.second.opBitsOne(condp->num());
return pairMaskBits;
}
// Determine whether we should check case items are complete
// Returns enum's dtype if should check, nullptr if shouldn't
static const AstEnumDType* getEnumCompletionCheckDType(const AstCase* const nodep) {
@ -191,18 +206,13 @@ class CaseVisitor final : public VNVisitor {
bool checkExhaustiveEnum(const AstCase* const nodep, const AstEnumDType* const enump) {
const uint32_t numCases = 1UL << nodep->exprp()->width();
for (AstEnumItem* eip = enump->itemsp(); eip; eip = VN_AS(eip->nextp(), EnumItem)) {
AstConst* const econstp = VN_AS(eip->valuep(), Const);
V3Number nummask{eip, econstp->width()};
nummask.opBitsNonX(econstp->num());
V3Number numval{eip, econstp->width()};
numval.opBitsOne(econstp->num());
const uint32_t mask = nummask.toUInt();
const uint32_t val = numval.toUInt();
const auto& match = matchPattern(nodep, VN_AS(eip->valuep(), Const));
const uint32_t mask = match.first.toUInt();
const uint32_t bits = match.second.toUInt();
// Check all cases to see if they cover this enum value/pattern
for (uint32_t i = 0; i < numCases; ++i) {
if ((i & mask) != val) continue; // This case is not for this enum value
if ((i & mask) != bits) continue; // This case is not for this enum value
if (m_caseDetails.records[i].itemp) continue; // Covered case
// Warn unless unique0 case which allows no-match
if (!nodep->unique0Pragma()) {
@ -278,19 +288,15 @@ class CaseVisitor final : public VNVisitor {
// Some items can never match due to 2-state simulation
if (neverItem(nodep, iconstp)) continue;
V3Number nummask{itemp, iconstp->width()};
nummask.opBitsNonX(iconstp->num());
V3Number numval{itemp, iconstp->width()};
numval.opBitsOne(iconstp->num());
const uint32_t mask = nummask.toUInt();
const uint32_t val = numval.toUInt();
const auto& match = matchPattern(nodep, iconstp);
const uint32_t mask = match.first.toUInt();
const uint32_t bits = match.second.toUInt();
bool foundNewCase = false;
const AstConst* firstOverlapConstp = nullptr;
uint32_t firstOverlapValue = 0;
for (uint32_t i = 0; i < numValues; ++i) {
if ((i & mask) != val) continue;
if ((i & mask) != bits) continue;
CaseRecord& caseRecord = m_caseDetails.records[i];
@ -498,17 +504,14 @@ class CaseVisitor final : public VNVisitor {
if (itemConstp->num().isFourState()
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
// Wildcard match, make 'caseExpr' & 'mask' == 'itemExpr' & 'mask'
V3Number numMask{itemp, itemConstp->width()};
numMask.opBitsNonX(itemConstp->num());
V3Number numOne{itemp, itemConstp->width()};
numOne.opBitsOne(itemConstp->num());
V3Number numRhs{itemp, itemConstp->width()};
numRhs.opAnd(numOne, numMask);
const auto& match = matchPattern(nodep, itemConstp);
const V3Number& matchMask = match.first;
const V3Number& matchBits = match.second;
VL_DO_DANGLING2(itemExprp->deleteTree(), itemExprp, itemConstp);
return AstEq::newTyped(
flp, //
new AstConst{flp, numRhs},
new AstAnd{flp, caseExprp, new AstConst{flp, numMask}});
new AstConst{flp, matchBits},
new AstAnd{flp, caseExprp, new AstConst{flp, matchMask}});
}
}

View File

@ -286,7 +286,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
// Get the mask that selects the bits that are relevant in this term
V3Number maskNum{srcp, m_width, 0};
maskNum.opBitsNonX(m_bitPolarity); // 'x' -> 0, 0->1, 1->1
maskNum.opBitsNonXZ(m_bitPolarity); // 'x' -> 0, 0->1, 1->1
const uint64_t maskVal = maskNum.toUQuad();
UASSERT_OBJ(maskVal != 0, m_refp,
"Should have been recognized as having const 0 result");

View File

@ -1310,7 +1310,7 @@ uint32_t V3Number::mostSetBitP1() const {
}
//======================================================================
V3Number& V3Number::opBitsNonX(const V3Number& lhs) { // 0/1->1, X/Z->0
V3Number& V3Number::opBitsNonXZ(const V3Number& lhs) { // 0/1->1, X/Z->0
// Correct number of zero bits/width matters
// op i, L(lhs) bit return
NUM_ASSERT_OP_ARGS1(lhs);

View File

@ -741,7 +741,7 @@ public:
// MATH
// "this" is the output, as we need the output width before some computations
V3Number& opBitsNonX(const V3Number& lhs); // 0/1->1, X/Z->0
V3Number& opBitsNonXZ(const V3Number& lhs); // 0/1->1, X/Z->0
V3Number& opBitsOne(const V3Number& lhs); // 1->1, 0/X/Z->0
V3Number& opBitsXZ(const V3Number& lhs); // 0/1->0, X/Z->1
V3Number& opBitsZ(const V3Number& lhs); // Z->1, 0/1/X->0

View File

@ -264,7 +264,7 @@ class UnknownVisitor final : public VNVisitor {
} else {
// X or Z's become mask, ala case statements.
V3Number nummask{rhsp, rhsp->width()};
nummask.opBitsNonX(VN_AS(rhsp, Const)->num());
nummask.opBitsNonXZ(VN_AS(rhsp, Const)->num());
V3Number numval{rhsp, rhsp->width()};
numval.opBitsOne(VN_AS(rhsp, Const)->num());
AstNodeExpr* const and1p = new AstAnd{nodep->fileline(), lhsp,