Generate decoder from ISA descriptor#725
Conversation
There was a problem hiding this comment.
Benchmarks
Details
| Benchmark suite | Current: 8b775f4 | Previous: f281b95 | Ratio |
|---|---|---|---|
Dhrystone |
1764.333 DMIPS |
1572.333 DMIPS |
0.89 |
CoreMark |
1158.742 iterations/sec |
1109.822 iterations/sec |
0.96 |
This comment was automatically generated by workflow using github-action-benchmark.
8ef6e5d to
17ba12d
Compare
| The generated decoder has two responsibilities for each instruction: | ||
|
|
||
| 1. **Identify** the instruction from its bit pattern (decision tree). | ||
| 2. **Extract** the operands (registers and immediates) into `rv_insn_t`. | ||
|
|
||
| For 32-bit instructions, identification uses nested switch statements on | ||
| the opcode, funct3, and funct7 fields. Operand extraction is handled by | ||
| type decoders (`decode_itype`, `decode_rtype`, etc.) inferred from the | ||
| operand names. | ||
|
|
||
| For 16-bit compressed (RVC) instructions, identification follows the same | ||
| switch-based decision tree. Operand extraction is per-instruction because | ||
| each RVC format has a unique bit layout for immediates. |
There was a problem hiding this comment.
You have to classify RISC-V extensions in advance.
There was a problem hiding this comment.
Restructured the extension table by RISC-V classification: base ISA (I), standard unprivileged extensions (M/A/F/C), and Z-extensions (Zicsr, Zifencei, Zba/Zbb/Zbc/Zbs).
42ae982 to
530a06e
Compare
|
Hi @jserv, I noticed a conflict in src/decode.c after #733 (RVV) was merged. Since this PR auto-generates decode.c from instructions.in, should I: |
The RVV decoder should be generated from extension definitions, which is precisely what this pull request aims to achieve. |
Replace the 2052-line hand-written src/decode.c with a generator-driven flow. The decoder is now produced by scripts/gen-decoder.py from a declarative ISA descriptor src/instructions.in, and decode.c becomes a generated artifact that is no longer maintained by hand. The motivation is to reduce ongoing maintenance cost. On master, the hand-written decoder receives commits both for adding extensions and for fixing bit-pattern mistakes: - 82205cb Fix FCVT illegal instruction handling - a00aeea Fix rounding mode selection - 52fe009 Fix illegal instruction handling for SLLI, SRLI, SRAI - ebf2362 Fix decode annotation error Such bugs arise because the bit patterns are duplicated in C code that must stay in sync with the spec. Driving decode.c from a single declarative source eliminates this entire class of bug by construction. Quantified maintenance cost reduction: - Hand-maintained decoder logic shrinks from 2052 lines (decode.c on master) to 239 lines (instructions.in), a reduction of 88 percent. scripts/gen-decoder.py adds 1019 lines of one-time tooling that is not touched when adding instructions. - Adding a new extension shrinks roughly five to six fold. On master, commit c10b895 added Zbb with +95 lines in decode.c and +21 in decode.h. The same change via the generator is about 20 declarative lines in instructions.in, copied directly from riscv-opcodes. Commit 29dabe4 added Zbs with +45 in decode.c and +11 in decode.h; the generator equivalent is 9 lines. Future maintenance workflow: - Adding one instruction is one line in instructions.in plus the execution semantics in emulate.c. Running make regenerates decode.c automatically. No edits to decode.c, decode.h, or gen-decoder.py are needed. - Adding a new extension adds one @extension tag in instructions.in followed by the instruction lines. The descriptor format and extension-addition workflow are documented in docs/decoder-generator.md. Close sysprog21#103
6680ff8 to
650012a
Compare
While migrating I noticed two encoding-level
Both fixes are minimal — Bug 1 is a one-line delete in |
Submit new pull request(s) to resolve the current RVV decoder. |
- cflwsp: rd=0 addresses f0 (valid float register), not x0. Remove from _RVC_RD0_RESERVED to fix FCZicsr arch-test failure. - decode_funct3: wrap in #if RV32_HAS(EXT_F) to fix unused-function error when building with ENABLE_EXT_F=0. - docs: remove history paragraph, classify extensions by RISC-V category (base ISA / standard / Z-extensions).
Add RVV operand classes (vd, vs1, vs2, vs3, vm, nf, veew, simm5, zimm5/10/11) and EXT_V / EXT_FV opcode groups to gen-decoder.py. Required by subsequent commits that register V-config and V-load/store instructions in src/instructions.in. Mixed-group decode (LOAD-FP / STORE-FP) falls back to itype / stype where the dominant fields are scalar; vector-specific operands are emitted per-leaf instead of through a non-existent decode_vtype helper.
Register vsetvli, vsetivli, and vsetvl in src/instructions.in so the auto-generated decoder dispatches the three V-config encodings under OP-V (opcode 0x15, funct3=7).
Enumerate every EEW x NF x MOP variant in src/instructions.in for unit-stride, fault-only-first, strided, indexed-unordered, indexed-ordered, whole-register, and mask loads/stores. Each variant becomes its own decode leaf, mirroring the official riscv-opcodes/rv_v listing rather than the runtime EEW-offset trick used by the previous hand-written decoder.
Add OP-V (opcode 0x15) integer/mask encodings to instructions.in: OPIVV / OPIVX / OPIVI (funct3=0/4/3) and OPMVV / OPMVX (funct3=2/6), covering funct6 0x00-0x1f and 0x20-0x3f. Handle three special dispatch patterns required by the encoding: - VWXUNARY0 (funct6=0x10, funct3=2): sub-dispatch on bits[19:15] - VMUNARY0 (funct6=0x14, funct3=2): same pattern - vmv.v.v / vmerge.vvm (funct6=0x17): disambiguated by vm bit[25]
Add OPFVV (funct3=1) and OPFVF (funct3=5) encodings: vfadd/vfsub/ vfmul/vfdiv, fused multiply-add family, reductions, sign-injection, slides, merge/move, and widening forms. The reserved funct6=0x21, funct3=1 encoding is intentionally decoded as vfrdiv.vf to preserve decode_v.c behavior; spec-correct illegal-instruction trapping will be addressed in a follow-up.
Remove src/decode_v.c (2286 lines); the auto-generated decoder in src/decode.c now covers all RVV v1.0 encodings previously handled by the hand-written decoder. Update docs/decoder-generator.md with EXT_V entries: - RVV operand reference (vd/vs1/vs2/vs3/vm/veew/zimm5/...) - OP-V funct6 / funct3 class conventions and constraint ordering - Special dispatch patterns (VWXUNARY0, VMUNARY0, vmv/vmerge) - Worked example for adding a new RVV instruction Two encoding bugs from decode_v.c are preserved verbatim for behavioral equivalence; a follow-up issue will track them.
The initial OPFVV/OPFVF generator covered funct6 < 0x18 and >= 0x20, missing the 0x18-0x1f range that holds floating-point comparison instructions producing mask results. Add 10 missing entries: - vmfeq.vv/.vf funct6=0x18 - vmfle.vv/.vf funct6=0x19 - vmflt.vv/.vf funct6=0x1b - vmfne.vv/.vf funct6=0x1c - vmfgt.vf funct6=0x1d (OPFVF only) - vmfge.vf funct6=0x1f (OPFVF only)
9c2fd3d to
8b775f4
Compare
Summary
Replace the hand-written instruction decoder in
src/decode.cwith onegenerated from
src/instructions.inviascripts/gen-decoder.py,closing #103.
Maintenance Cost Reduction (Quantified)
Code that requires human maintenance
src/decode.c(hand-written / generated)src/instructions.in(declarative ISA descriptor)scripts/gen-decoder.py(one-time tooling)The 1019 lines in
gen-decoder.pyare a one-time investment. Adding anew instruction or extension touches only
instructions.in, nevergen-decoder.py. The fair comparison for ongoing maintenance is2052 → 239 lines.
Cost of adding a new extension (decoder side only)
Measured against two recent extension-addition commits on master:
decode.cdecode.hWith the generator, adding the same extensions touches only
instructions.in:@extensiontag + 8 instructions)@extensiontag + 19 instructions)That is a ~5–6× reduction in decoder-side diff size, and the diff
is now declarative bit-pattern data lifted directly from the
riscv-opcodes repo, instead
of hand-written
if/switchchains that must be reasoned aboutcase-by-case.
Bug class eliminated by construction
git logondecode.cshows 36 commits, of which a recurring classis decode-table mistakes:
82205cbFix FCVT illegal instruction handlinga00aeeaFix rounding mode selection52fe009Fix illegal instruction handling for SLLI, SRLI, SRAIebf2362Fix decode annotation errorThese are mismatches between the spec and hand-written bit-pattern
matching. Generating
decode.cfrom a single declarative sourceremoves this entire class — there is no second hand-maintained copy
of the bit patterns to drift out of sync with the spec.
Workflow change for future maintainers
Before (hand-written decoder): adding one instruction requires
edits in 5+ places —
decode.henum,decode.cswitch case (oftennested inside the right
op_xxxhandler), the right#if RV32_HAS()guard, plus execution semantics. Easy to miss the guard or get the
funct3/funct7 nesting wrong.
After (this PR): adding one instruction is one line in
instructions.in(copy from riscv-opcodes), plus execution semanticsin
emulate.c.makeregeneratesdecode.cautomatically. Adding awhole new extension is the same plus one
@extensiontag.docs/decoder-generator.mddocuments the descriptor format and theextension-addition workflow.
Verification
ENABLE_*=0extension-disable builds pass under bothdefconfigandjit_defconfig(matches the CI matrix in.github/workflows/main.yml).make check(puzzle, fcalc, pi) passes.make tests(cache, map, path) passes.