Skip to content

spec: Field VM - #971

Merged
RobinJadoul merged 33 commits into
spec/mainfrom
spec/recursion.vm
Oct 2, 2026
Merged

RobinJadoul merged 33 commits into
spec/mainfrom
spec/recursion.vm

Conversation

@RobinJadoul

Copy link
Copy Markdown
Collaborator

No description provided.

@RobinJadoul RobinJadoul self-assigned this Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

Kimi Code Review

⚠️ Review failed: Kimi API request failed with status 401


Automated review by Kimi (Moonshot AI)

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Code Review

Found five issues in the changed specification/tooling:

  • High — Register preservation is missing (field_vm.toml:174). The polynomial constrains pc' - (pc + 1) instead of registers'[i] - registers[i]. When hint_output = 1, unrelated registers can change freely; ordinary jumps are also forced to fall through. Use the register difference shown in the accompanying constraint.

  • High — FMA polynomial implements multiplication of three operands (field_vm.toml:156). It enforces o = a * b * c, rather than o = a * b + c. Move args[3] outside the multiplication. For example, the documented MUL expansion has c = 0, so this polynomial forces its result to zero.

  • Medium — New type breaks specification validation (config.toml:11). ExtField references itself before being registered. Running the checker fails immediately with Couldn't lookup type by name: 'ExtField'. Add explicit extension-field support or a supported representation.

  • Medium — Register multiplexing has invalid references (field_vm.toml:85). The power wrapper uses arg_reg_pows_computed[j][j-1] instead of [i][j-1], sharing argument 1’s power across all operands. Additionally, MUX has six entries but is indexed through six inclusive, and the power constraint references undefined arg_registers. Correct these to preserve register selection.

  • Medium — PC transition polynomial contains an unbound index (field_vm.toml:187). This constraint uses i without an iterator and incorrectly includes hint_input[i]. Remove that factor and use MUX[1], matching the stated PC-selector constraint.

@RobinJadoul

Copy link
Copy Markdown
Collaborator Author

Getting the typechecker to be happy with ExtField elements is one of the next steps ;)

@erik-3milabs erik-3milabs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary:

  • instruction and constraints are similar, but not the same. Sketching the right mental model is informative.
  • an instruction spans multiple states, which is a new concept in this spec. The reader is not helped to understand this. Sketching the right mental model is informative.
  • the concepts of input hinting, output hinting and register hinting and their relation is unclear.
  • have another look at the lower and upper bounds of sum. There seem to be some issues with that.
  • minor notes/rephrase proposals

Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/src/field_vm.toml Resolved
Comment thread spec/expr.typ Resolved
Comment thread spec/expr.typ Resolved
Comment thread spec/src/field_vm.toml Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Outdated
== Register hinting

Each general-purpose register in the current state can be marked as _hinted_ by the acting instruction.
This means that from the current state onwards, the register can take a value

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you choosing "onwards" here? It suggests that if for one instruction you set a register to hintable, and it remains that way for the rest of the execution trace. Perhaps worth clarifying that this is a way for the transition constraint not to hold for that particular instruction.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onwards is there to avoid making it seem like it's only for this state, i.e. that the transition constraints do carry it forward and it's not a temporary exception.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a footnote to hopefully clarify a bit. We don't really have "transition constraints" yet at this point of the expose.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't really have "transition constraints" yet at this point of the expose. [...] The onwards is there to avoid making it seem like it's only for this state, i.e. that the transition constraints do carry it forward and it's not a temporary exception.

Should we then not start this section with outlining that, in principal, register values of contiguous states are the same (referring to the state machine description at the start of 35.1.1).
That furthermore allows us to explain why hinting should be included in the first place: to permit exceptions to this rule. And then clarify that indicating a "hint" just means severing this "continuity requirement" for one state transition.

wdyt?

Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
Comment thread spec/chapters/field_vm.typ Resolved
@erik-3milabs erik-3milabs added the spec Updates and improvements to the spec document label Sep 21, 2026
Comment thread spec/chapters/field_vm.typ Outdated
`MUL d, a, b`][`FMA d == a * b + (0 * X), hint out`][Multiplication][
`INV d, a`][`FMA d == (a + 1) * d - 1, hint <register of d>`][Extension field inversion. Note: `d` and `a` cannot use the same register here, and `d` should not be input-hinted in the next instruction.][
`J a`][`FMA PC == a, hint out`][Jump. Can be to a register, memory content, or absolute address, depending on the addressing mode of `a`, even relative to PC][
`JZA imm`][`FMA PC == (ZERO)*(-1*PC+(imm-1))+(1*PC+1), hint out`][Jump if ZERO, absolute target address][

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A) I'm not exactly sure what you mean here, the notation is meant to imply that in the translation of JZA, the immediate used is modified and not copied as-is.

Let me rephrase.

Denoting -1*PC+(imm-1) in JZA suggests that this argument has the form imm_0 * X + imm_1 - imm_2, where imm_0 = -1, X = PC and imm_2 = 1; it does not cleanly fit the imm_0 * X + imm_1 format

Same holds for JNZA, whose notation suggests that the b-argument has either the form

  1. imm_0 * X + imm_1 - imm_2, with imm_1 = 0, or
  2. imm_0 * X + imm_1 * imm_2, with imm_1 = -1.

B) Ok, I see. Why do you prefer this way of writing over, say,

#table(
    columns: (auto, auto, auto)
    [a][b][c]
    [d][e][f]
)

?

Comment thread spec/chapters/field_vm.typ Outdated

Since the VM makes use of read-only memory, traditional usage of a program stack does not work.
We assume that each function invocation (unless other optimizations apply) will have an associated _frame_,
pointed to by a _frame pointer_ `fp`, one of the general purpose registers.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of rephrasing

  1. "... pointed to by a frame pointer ..." to "... pointed to by the frame pointer ...", and
  2. "we assume here that fp is one of the general purpose registers" to "we assume one of the general purpose registers to assume the role of fp"
    ?

(github doesn't support suggestions on outdated comments :grumble:)

Comment thread spec/chapters/field_vm.typ Outdated
we perform a _"degree split"_:
$ f_(i)(x) = f_(i, 0)(x) + x^(d - 1) (f_(i, 1)(x) + x^(d - 2) (f_(i, 2) + x^(d - 2) (f_(i, 3) + ...))), $
$ f_(i)(x) &= f_(i, 0)(x) + x^(d - 1) (f_(i, 1)(x) + x^(d - 2) (f_(i, 2) + x^(d - 2) (f_(i, 3) + ...)))\
&= 1 dot f_(i,0)(x) + x^(d - 1) f_(i,1)(x) + ... + x^(d - 1 + (t - 1) dot (d - 2)) f(i, t)(x), $

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
&= 1 dot f_(i,0)(x) + x^(d - 1) f_(i,1)(x) + ... + x^(d - 1 + (t - 1) dot (d - 2)) f(i, t)(x), $
&= 1 dot f_(i,0)(x) + x^(d - 1) f_(i,1)(x) + ... + x^(d - 1 + (t - 1) dot (d - 2)) f_(i, t)(x), $

Comment thread spec/chapters/field_vm.typ Outdated
== Register hinting

Each general-purpose register in the current state can be marked as _hinted_ by the acting instruction.
This means that from the current state onwards, the register can take a value

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't really have "transition constraints" yet at this point of the expose. [...] The onwards is there to avoid making it seem like it's only for this state, i.e. that the transition constraints do carry it forward and it's not a temporary exception.

Should we then not start this section with outlining that, in principal, register values of contiguous states are the same (referring to the state machine description at the start of 35.1.1).
That furthermore allows us to explain why hinting should be included in the first place: to permit exceptions to this rule. And then clarify that indicating a "hint" just means severing this "continuity requirement" for one state transition.

wdyt?

Comment on lines +136 to +138
```
FMA [1 * X + 2] == [3 * Y + 4] * (5 * Z) + [W + 6], hint out + Z
```

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this overview supposed to be complete, or just an example? I.e., does Z have an additional sum imm and W a product imm?

Comparing this overview to the below table suggests some of the operations cannot actually be mapped onto this overview.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an example where I wanted to highlight some options to simplify notation for the common case of imm_0 ∈ {0,1} or imm_1 = 0

Comment thread spec/chapters/field_vm.typ Outdated
A state is then the tuple of all values the registers have at a given point in time during the execution.#footnote[
If we consider states across different executions, the contents of `MEM` should also be considered part of the state.
]
Each instruction acts upon _current state_ to produce the _future state_.#footnote[

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Each instruction acts upon _current state_ to produce the _future state_.#footnote[
Each instruction acts upon the _current state_ to produce the _future state_.#footnote[

Comment thread spec/chapters/field_vm.typ Outdated
Comment on lines +61 to +63
The `PC` register stores the program counter: the address of the current instruction,
and --- except when branches are taken, through register hinting --- is incremented by $1$
for every consecutive state.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I don't follow the "..., and --- [...] --- is incremented ..."-segment all that well,
  2. I find the comma in the em-dashed sentence hard to read.

what about

Suggested change
The `PC` register stores the program counter: the address of the current instruction,
and --- except when branches are taken, through register hinting --- is incremented by $1$
for every consecutive state.
The `PC` register stores the program counter: the address of the current instruction.
Almost every instruction increments this value by $1$, except those taking a branch.

or a variation on this?

Comment thread spec/chapters/field_vm.typ Outdated
Comment on lines +74 to +77
By default, a general-purpose register will have the same value the future state
as it does in the current state.
To change register values, each general-purpose register in the current state
can be marked as _hinted_ by the acting instruction.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. At the very least:
Suggested change
By default, a general-purpose register will have the same value the future state
as it does in the current state.
To change register values, each general-purpose register in the current state
can be marked as _hinted_ by the acting instruction.
By default, a general-purpose register will have the same value in the future state
  1. The concepts of current and future state only exist in the context of an instruction. I recommend that we either:
    i. mention the instruction as context in the first sentence, or
    ii. start more generic by using "consecutive states" or "contiguous states".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully agree on the existence of the states being dependent on an instruction, but I also agree that something like "across consecutive states" probably reads better for the first sentence.

Comment thread spec/chapters/field_vm.typ Outdated
Here all of `d`, `a`, `b` and `c` are arguments following the addressing scheme described below.

This constraint-based view generally goes well with a read-only memory.
The memory system gives us that guarantee that whenever we access `MEM` at the same index,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The memory system gives us that guarantee that whenever we access `MEM` at the same index,
The memory system gives us the guarantee that whenever we access `MEM` at the same index,

Comment thread spec/chapters/field_vm.typ Outdated
the register can take a value that is independent from the previous value,
except as constrained by the instruction.
Additionally, the _output_ can be marked as hinted, meaning that the register used in the `d` argument
will change from the future state onwards, and as such in the `d` argument too.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will change from the future state onwards, and as such in the d argument too.

The use of "as such ... too" suggests that the d argument will change in the future state onwards? Idk, sounds weird.
What about

Suggested change
will change from the future state onwards, and as such in the `d` argument too.
will change from the future state onwards, thus impacting the `d` argument of the current transition.

?

Comment thread spec/chapters/field_vm.typ Outdated
on the result.
There may even be situations where hinting multiple values can be chosen simultaneously, such as a decomposition
$a = b + c$ in a divide-and-conquer algorithm.
Even hinting registers that are not used in the current instruction may provide useful in limited situations.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Even hinting registers that are not used in the current instruction may provide useful in limited situations.
Even hinting registers that are not used in the current instruction may prove useful in limited situations.

implementers and practitioners are encouraged to discover and use their own,
as experience may point out further useful abstractions.

#table(columns: (auto, 2fr, 1fr),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omitting my comments on this table, as those are being discussed in our chat.

Comment thread spec/chapters/field_vm.typ Outdated
We again make use of the multiplexing machinery from before.
The constraints we want to enforce on a register index $r$ are as follows:
- $!next("hint_input")_r and !#`hint_output` => next("registers")_r = #`registers`_r$,\ `r` could not have been hinted,
since it was not input-hinted in the next row, and there was no output hint, so the next `r` should remain the same.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
since it was not input-hinted in the next row, and there was no output hint, so the next `r` should remain the same.
since it was not input-hinted in the next row, and there was no output hint, so `r'` should remain the same.

Comment thread spec/src/field_vm.toml Outdated
Comment on lines +141 to +142
# PARAM: $limits(sum)_(j = 0)^T #`arg_register_powers`_(i,j) dot limits(sum)_(k = 0)^(d - 1) #`argument_registers`_i^k dot #`RANGE`_(j,k)$
constraint = "$limits(sum)_(j = 0)^1 #`arg_register_powers`_(i,j) dot limits(sum)_(k = 0)^4 #`argument_registers`_i^k dot #`RANGE`_(j,k)$"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing = 0?

@erik-3milabs erik-3milabs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my remaining remarks are so minor, that I think this is ready to be merged!

Comment thread spec/chapters/decode.typ Outdated
Comment on lines +128 to +129
([`DIV[U][W] rd, rs1, rs2`], [`DIVREM`], [`[W]`], [#sym.not`[U]`], [], [@decode:note:word_instr, <note_signed>]),
([`REM[U][W] rd, rs1, rs2`], [`DIVREM`], [`[W]`], [#sym.not`[U]`], [`muldiv_selector`], [@decode:note:word_instr, <note_signed>]),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
([`DIV[U][W] rd, rs1, rs2`], [`DIVREM`], [`[W]`], [#sym.not`[U]`], [], [@decode:note:word_instr, <note_signed>]),
([`REM[U][W] rd, rs1, rs2`], [`DIVREM`], [`[W]`], [#sym.not`[U]`], [`muldiv_selector`], [@decode:note:word_instr, <note_signed>]),
([`DIV[U][W] rd, rs1, rs2`], [`DIVREM`], [`[W]`], [#sym.not`[U]`], [], [@decode:note:word_instr@decode:note:signed]),
([`REM[U][W] rd, rs1, rs2`], [`DIVREM`], [`[W]`], [#sym.not`[U]`], [`muldiv_selector`], [@decode:note:word_instr@decode:note:signed]),

Comment thread spec/meta.typ Outdated

#let notelist(prefix, body) = {
show figure.where(kind: "note"): it => {
super(it.counter.display(it.numbering)) + box(it)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this box(it) doesn't seem to do much here

Comment thread spec/chapters/field_vm.typ Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the pdf, address overflows to the next line. Maybe break here and put // address on the next line?

Comment thread spec/meta.typ
show math.equation.where(block: false): box // Don't line-break inline equations by default
show ref: equate.with(sub-numbering: true, breakable: true, number-mode: "label")
show ref: it => if it.element != none and it.element.func() == figure and it.element.kind == "note" {
super[[#it]]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed curious that it does not include the brackets from the numbering here...

@RobinJadoul
RobinJadoul marked this pull request as ready for review September 30, 2026 08:10
@github-actions

Copy link
Copy Markdown

Kimi Code Review

⚠️ Review failed: Kimi API request failed with status 401


Automated review by Kimi (Moonshot AI)

@github-actions

Copy link
Copy Markdown

Codex Code Review

No actionable issues found in the PR diff. The specification validator passes. Changes are limited to specification documents and tooling; no Rust runtime code changed.

Base automatically changed from spec/recursion to spec/main October 2, 2026 12:14
@RobinJadoul
RobinJadoul merged commit 41c3af9 into spec/main Oct 2, 2026
2 checks passed
@RobinJadoul
RobinJadoul deleted the spec/recursion.vm branch October 2, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

spec Updates and improvements to the spec document

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants