fix(verilog): a real value must not travel in an integer container (+6 declared tests, 0 regressions) - #2991
Merged
Merged
Conversation
Two sites, one class, both found by pointing `t27c icarus-simulate` at a spec for the first time -- the repository's only ruler that RUNS generated Verilog, and one that has had zero targets since specs/scratch was untracked (#2987). 1. `gen_verilog_expr` sent every `*` through `__mul_noop`, the shift-and-add ladder added by #741 so synthesizable RTL carries no `*` operator (R-SI-1). The ladder is declared `input [63:0]`, so a float operand is rounded before use: `__mul_noop(0.3, 10.0)` is 0, and `ewma_step(5.0, 0.3, 10.0)` returned 5.0 where C, Rust, Zig and hand arithmetic all say 6.5. R-SI-1 governs SYNTHESIZABLE RTL; Verilog `real` is a simulation-only type no synthesis flow accepts, so a real multiply was never in that rule's subject. 2. A `given` binding was emitted `reg [63:0] e;` whatever its initializer returned, so `e = ewma_step(0.5, 0.5, 1.0)` stored 1 instead of 0.75. Measured, `specs/trinet/etx.t27` under `icarus-simulate`: before 5 PASSED / 6 FAILED, exit 1 after 11 PASSED, exit 0 Corpus: 46 of 581 generated .v change; `iverilog -g2012` accepts 380 before and 380 after. Acceptance does not move because the ladder always produced valid Verilog -- it computed something else. Seals for the 46 are re-sealed here, in this commit, with that acceptance reading as the reason. Two corrections to my own work, kept because they are the finding: * I predicted three tests would flip from the multiply fix alone and that the three `alpha_*` would not move. One flipped; all six moved once the binding was fixed too. The prediction was wrong in both directions and the two defects are one class at two sites. * The `ExprLiteral` arm first read `extra_kind == "float" || value.contains('.')`. Nothing in this compiler ever sets `extra_kind` to "float" -- a condition invented rather than read. It is gone. The mutation that found it was itself broken first (`false && A || B` disables only the left disjunct), so the arm looked dead when the instrument was. Mutation-checked, 5 of 5 killed: always-ladder, always-reg, everything-is-real, a-call-is-never-real, a-literal-is-never-real. `verilog_r_si_1.rs` is the control that the integer path is unchanged; it stays green. `rustfmt --check` reports 315 complaints on compiler.rs before and 315 after. Closes #2990 Refs #741, #2987
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
Contributor
PR DashboardGenerated at: 2026-08-30 21:22:10 UTC
Summary
Seal Status
|
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
ci-gates 422-425: an artifact control is not an emitter repair; a boolean mutation that respects precedence; a condition invented rather than read; and a test passing on the one input that survives the bug. Refs #2990
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
PR DashboardGenerated at: 2026-08-30 21:23:17 UTC
Summary
Seal Status
|
check_now_entry_shape.py requires `# NOW -- <title> (YYYY-MM-DD)` as the first line plus a `## ` section heading. docs/now/README.md describes only the OTHER gate's three conditions (presence, freshness, a heading and a bullet), so the shape this one enforces is not written down beside it. Refs #2990
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
PR DashboardGenerated at: 2026-08-30 21:30:28 UTC
Summary
Seal Status
|
…ce printed
The skip guard tested `log.contains("not found")`. The runner prints
`No such file or directory (os error 2)`, so the guard never fired and the
assertion ran against an error message -- test-ratchet named this test as newly
failing. A skip path written for one environment and never executed in it.
Both legs are now run before committing: with iverilog on PATH the runtime leg
executes and passes; with it stripped the test prints its reason and passes.
ci-gates 426.
Refs #2990
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
PR DashboardGenerated at: 2026-08-30 21:34:07 UTC
Summary
Seal Status
|
gHashTag
added a commit
that referenced
this pull request
Aug 30, 2026
…cking gate
Measured with a committed entry in the wrong shape:
tri hooks now-gate exit 0 "NOW gate PASSED"
t27c check-now exit 0 "build authorized"
scripts/verify.sh "gates:OK"
check_now_entry_shape exit 1 "FAIL: 1 of 3 entr(y/ies) do not say
anything checkable"
The last is the REQUIRED `check` context. docs/now/README.md documents the other
gate's three conditions and then says the same three are what the local tools
enforce; the shape condition -- a `# NOW -- <title> (YYYY-MM-DD)` first line and
a `## ` section heading -- is previewed by nothing. The two local tools also name
a different entry than the one the change adds: they answer "is the newest entry
fresh", not "is the added entry well formed".
Found by paying for it: this made `check` red on #2991.
Refs #2987
gHashTag
added a commit
that referenced
this pull request
Aug 30, 2026
…RUN (#2993) * feat(tri): vsim funnel -- how far each spec gets when its Verilog is RUN The one arm that can catch a defect whose nature is that it COMPILES is the one that simulates, and its gate has had no targets since specs/scratch was untracked in #2283 (#2987). Pointing it at the corpus by hand produced four defects in one sitting: #2988, #2989, #2990 and #2869. Repairing the gate is half a line and half a decision about 260 baselines for subjects that no longer exist. The measurement should not wait for it. Measured at 487003f over 650 specs: refused by gen-verilog-for-simulation 69 iverilog rejected the testbench 410 simulation exited non-zero 0 ran and REPORTED FAILURES 10 ran and produced a verdict 106 ran, exit 0, and said NOTHING 55 650 of 650 accounted for. Three properties worth naming: * is its own row. run_icarus_simulate bails only on a line saying FAILED, so `checked everything and passed` and `checked nothing` are the same exit code. A spec there is not passing; nothing asked it anything. * One TMPDIR per spec, because run_icarus_simulate keys its scratch file on the BASENAME alone and this corpus shares stems. * It refuses when iverilog or t27c is absent rather than printing a table it did not earn, and asks the OS rather than matching an error message (ci-gates 426). It reports and never gates: which of the 10 are compiler defects and which are spec defects is not a question a walker may answer. Refs #2987 * docs: correct the previous commit message, and close the backtick class The message for `tri vsim funnel` was passed inline to `git commit -m "..."`. Double quotes do not stop command substitution, so the span naming the `silent` row was executed and removed: * `silent` is its own row. -> * is its own row. zsh printed `command not found: silent`, which is the only reason it was seen. The commit is pushed and this repository forbids force-pushing, so the message stands with the hole and this is the correction. The missing word is `silent` -- the row for a spec that ran, exited 0, and printed no verdict line at all. ci-gates 418 already says to quote a heredoc delimiter when the body carries a backtick. That is too narrow: this went through the argument list instead. 427 names the class -- prose containing a backtick never reaches the shell as an argument; write it to a file or pipe it through a QUOTED heredoc and let git read it with `-F -`. Third occurrence in this repository's log, which by its own rule means the cure was wrong rather than that this is another case. Refs #2987 * docs(now): record that the local NOW preview does not preview the blocking gate Measured with a committed entry in the wrong shape: tri hooks now-gate exit 0 "NOW gate PASSED" t27c check-now exit 0 "build authorized" scripts/verify.sh "gates:OK" check_now_entry_shape exit 1 "FAIL: 1 of 3 entr(y/ies) do not say anything checkable" The last is the REQUIRED `check` context. docs/now/README.md documents the other gate's three conditions and then says the same three are what the local tools enforce; the shape condition -- a `# NOW -- <title> (YYYY-MM-DD)` first line and a `## ` section heading -- is previewed by nothing. The two local tools also name a different entry than the one the change adds: they answer "is the newest entry fresh", not "is the added entry well formed". Found by paying for it: this made `check` red on #2991. Refs #2987 --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(verilog): a real value must not travel in an integer container
Two sites, one class, both found by pointing
t27c icarus-simulateat a specfor the first time -- the repository's only ruler that RUNS generated Verilog,
and one that has had zero targets since specs/scratch was untracked (#2987).
gen_verilog_exprsent every*through__mul_noop, the shift-and-addladder added by wave-27: t27c gen-verilog emits __mul_noop(a, b) instead of bare
*(R-SI-1) #741 so synthesizable RTL carries no*operator (R-SI-1).The ladder is declared
input [63:0], so a float operand is rounded beforeuse:
__mul_noop(0.3, 10.0)is 0, andewma_step(5.0, 0.3, 10.0)returned5.0 where C, Rust, Zig and hand arithmetic all say 6.5. R-SI-1 governs
SYNTHESIZABLE RTL; Verilog
realis a simulation-only type no synthesisflow accepts, so a real multiply was never in that rule's subject.
A
givenbinding was emittedreg [63:0] e;whatever its initializerreturned, so
e = ewma_step(0.5, 0.5, 1.0)stored 1 instead of 0.75.Measured,
specs/trinet/etx.t27undericarus-simulate:before 5 PASSED / 6 FAILED, exit 1
after 11 PASSED, exit 0
Corpus: 46 of 581 generated .v change;
iverilog -g2012accepts 380 before and380 after. Acceptance does not move because the ladder always produced valid
Verilog -- it computed something else. Seals for the 46 are re-sealed here, in
this commit, with that acceptance reading as the reason.
Two corrections to my own work, kept because they are the finding:
three
alpha_*would not move. One flipped; all six moved once the bindingwas fixed too. The prediction was wrong in both directions and the two
defects are one class at two sites.
ExprLiteralarm first readextra_kind == "float" || value.contains('.').Nothing in this compiler ever sets
extra_kindto "float" -- a conditioninvented rather than read. It is gone. The mutation that found it was itself
broken first (
false && A || Bdisables only the left disjunct), so the armlooked dead when the instrument was.
Mutation-checked, 5 of 5 killed: always-ladder, always-reg, everything-is-real,
a-call-is-never-real, a-literal-is-never-real.
verilog_r_si_1.rsis thecontrol that the integer path is unchanged; it stays green.
rustfmt --checkreports 315 complaints on compiler.rs before and 315 after.Closes #2990
Refs #741, #2987
Found by the sweep that produced #2987, #2988, #2989 and #2990. The other three
are filed, not fixed: they need machinery this backend does not have (a named
block per loop) or a decision about 260 baselines.
🤖 Generated with Claude Code