Optimize wide decoder case statements into decoder expressions (#7804)

Extend the decoder-pattern case optimization to selectors that are too
wide for a full 2^width lookup table. A decoder-pattern case (where
every case item assigns constants to a fixed set of LHSs) is lowered to
a new AstMachMasked expression. AstMachMasked is emitted as a run-time
VL_MATCHMASKEd_* function call. It contains a packed constant pool table,
'matchp', which is a list of '(mask, bits)' pairs. At runtime, the index of the 
first matching entry is returned, and is used to index a value table. This single
(albeit complicated) expression can replace large if-else trees whole, resulting
in much more compact code with fewer static hard to predict branches. It
is worth about 10% speed and 30% code size in some designs.

Example:

```systemverilog
    logic [39:0] sel;
    always_comb
      casez (sel)
        40'b???????????????????????????????????????1: out = 8'h01;
        40'b??????????????????????????????????????1?: out = 8'h02;
        40'b?????????????????????????????????????1??: out = 8'h03;
        default:                                      out = 8'hff;
      endcase
```

is compiled to:

```c++
    out = TABLE_value[VL_MATCHMASKED_Q(sel, CONST_match)];
```

Where 'CONST_match' contains 4 entries, of a 40-bit mask and 40-bit bit
pattern each, and 'TABLE_value' contains 4 entries of the corresponding
8-bit results. (Entries are aligned to word boundaries to avoid runtime
bit swizzling)
This commit is contained in:
Geza Lore
2026-06-19 19:46:13 +01:00
committed by GitHub
parent 59fba72cb6
commit a37e2ee94b
34 changed files with 1114 additions and 87 deletions
+23
View File
@@ -402,6 +402,29 @@ class AstToDfgConverter final : public VNVisitor {
DfgVertex* const vtxp = make<DfgCReset>(nodep->fileline(), *dtypep);
nodep->user2p(vtxp);
}
void visit(AstMatchMasked* nodep) override {
UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting");
UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");
if (unhandled(nodep)) return;
const DfgDataType* const dtypep = DfgDataType::fromAst(nodep->dtypep());
if (!dtypep) {
m_foundUnhandled = true;
++m_ctx.m_conv.nonRepDType;
return;
}
iterate(nodep->lhsp());
if (m_foundUnhandled) return;
iterate(nodep->matchp());
if (m_foundUnhandled) return;
FileLine* const flp = nodep->fileline();
DfgMatchMasked* const vtxp = make<DfgMatchMasked>(flp, *dtypep);
vtxp->lhsp(nodep->lhsp()->user2u().to<DfgVertex*>());
vtxp->matchp(nodep->matchp()->user2u().to<DfgVertex*>());
nodep->user2p(vtxp);
}
void visit(AstReplicate* nodep) override {
UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting");
UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");