Add RISC-V compressed instruction support#649
Conversation
Codex Code ReviewFindings
Verification I could not run the targeted cargo tests because Rustup attempted to write under the read-only home directory:
|
|
Benchmark Results for unmodified programs 🚀
|
1558952 to
c0c39cb
Compare
| src2: rs2, | ||
| op: ArithOp::Add, | ||
| }), | ||
| _ => Err(InstructionError::IllegalCompressed(half)), |
There was a problem hiding this comment.
Low – Unreachable arm (dead code)
The match on ((h >> 12) & 1, rs2) is exhaustive: the first element is 1-bit (0 or 1), and (0, 0) / (0, _) / (1, 0) / (1, _) cover every possible tuple. The _ => arm can never be reached. Consider replacing it with unreachable!() so the compiler can flag any future gap, rather than silently swallowing it as a valid error path:
| _ => Err(InstructionError::IllegalCompressed(half)), | |
| _ => unreachable!("all CR patterns covered"), |
| Err(e) => break e, | ||
| } | ||
| }; | ||
| assert!( |
There was a problem hiding this comment.
Low – Weak negative assertion
The original test positively asserted InstructionAddressMisaligned(2), which provided a precise contract. The replacement only asserts the error is not a misalignment trap — any other ExecutorError (including hypothetical future bugs producing the wrong variant) would satisfy it. Consider adding a positive assertion of the expected error kind, e.g.:
assert!(
matches!(
err,
ExecutorError::MemoryError(_)
| ExecutorError::InstructionError(_)
| ExecutorError::InvalidInstruction(_)
),
"expected a decode/memory error at pc=2, got {:?}",
err
);(Use whichever ExecutorError variant is produced by a bad decode at that address.)
| // is not a multiple of 4). It is never a real instruction start, so skip it | ||
| // instead of failing the whole segment decode. If such a slot is ever the | ||
| // target of a jump, the on-demand fetch path will surface the error. | ||
| if lo == 0 { |
There was a problem hiding this comment.
Medium – Silent skip creates prover/executor asymmetry for c.unimp
Skipping 0x0000 is logically correct (it can never be the first halfword of a 4-byte instruction since bits[1:0] would need to be 11), but the behaviour differs between paths:
- Segment pre-decode (this path): the address is not inserted into the DECODE table.
- On-demand fetch (execution.rs,
lo = 0x0000): callsdecompress(0x0000)→IllegalCompressed.
This means a jump to a c.unimp in a pre-decoded segment hits the on-demand path at runtime (cache miss → fetch → error), which is correct. However, any ELF where the compiler deliberately emits c.unimp as a trap instruction will silently drop those addresses from the instruction cache. If the prover ever needs to prove an execution that touches such an address (it can't — the executor would error first), the DECODE table wouldn't have the row.
The current behaviour is safe, but it's worth noting: decode_segment_words and decompress treat 0x0000 differently (skip vs. error). A future change to pre-populate the DECODE table for all reachable addresses (including traps) would need to revisit this skip.
Review: Add RV64C compressed instruction supportOverall: Well-implemented. The bit-manipulation for immediate decoding was verified against the RISC-V unprivileged ISA spec tables — all scramble patterns are correct (CI, CJ, CB, CL/CS immediates). The prover integration (c_type flag, single Issues foundLow – Dead code in Low – Weak negative assertion in Medium – Silent skip of 0x0000 in Minor observations (no fix needed)
|
No description provided.