Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registers in memory #1443

Merged
merged 45 commits into from
Jul 22, 2024
Merged

Registers in memory #1443

merged 45 commits into from
Jul 22, 2024

Conversation

leonardoalt
Copy link
Member

@leonardoalt leonardoalt commented Jun 11, 2024

Fixes #1492

@leonardoalt leonardoalt changed the title Registers in memory rebase attempt Registers in memory Jun 13, 2024
@leonardoalt leonardoalt force-pushed the registers-in-memory-dev branch 2 times, most recently from 2284f46 to 2aedf98 Compare June 14, 2024 11:35
@leonardoalt leonardoalt changed the base branch from main to instr_link June 14, 2024 11:36
@leonardoalt leonardoalt force-pushed the registers-in-memory-dev branch 5 times, most recently from 48195ed to 8b67cba Compare June 18, 2024 16:29
Base automatically changed from instr_link to main June 18, 2024 18:00
@leonardoalt leonardoalt force-pushed the registers-in-memory-dev branch 5 times, most recently from 2a1b8f5 to db4fcbb Compare June 26, 2024 18:24
github-merge-queue bot pushed a commit that referenced this pull request Jul 8, 2024
Implements #1055 for the Poseidon machines. Pulled out of #1508.

Specifically, this PR adds a new `PoseidonGLMemory` machine which
receives 2 memory points and then:
- Reads 24 32-Bit words and packs them into 12 field elements
- Computes the Poseidon permutation (just like `PoseidonGL`)
- For each of the 4 output field elements, it:
- Invokes the `SplitGL` machine to get the canonical `u64`
representation
  - Writes the 8 32-Bit words to memory at the provided memory pointer

The read and write memory regions can even overlap! 🎉 

This should simplify our RISC-V machine, as the syscall already expects
two memory pointers. We can simply pass it to the machine directly.

I started doing that in #1533, but I think it makes sense to wait until
#1443 is merged.

To test:
```
cargo run -r pil test_data/std/poseidon_gl_test.asm -o output -f --export-csv --prove-with estark-starky
```

I recommend reviewing the diff between
`std/machines/hash/poseidon_gl.asm` and
`std/machines/hash/poseidon_gl_memory.asm`

### Discussion

The overhead of the memory read / write is quite high (18 extra witness
columns, see [this
comment](https://github.com/powdr-labs/powdr/blob/40bdca4368c3accccb753aa35ac1027ccb8def0e/std/machines/hash/poseidon_gl_memory.asm#L13-L23),
mostly because we now need to have the input available in all rows
(which previously was only the case for the outputs). If we had offsets
other than 0 and 1, this could be avoided. Doing 24 parallel memory
reads in the first row would *not* help, because we'd have to add 24
witness columns (instead of 2 now) to store the result of the memory
operation.

A few more notes:
- With Vadcop, 18 extra witness columns in a secondary machine is *a
lot* better than introducing more registers (either "regular" registers
or assignment registers) in the main machine
- As mentioned
[here](https://github.com/powdr-labs/powdr/blob/40bdca4368c3accccb753aa35ac1027ccb8def0e/std/machines/hash/poseidon_gl_memory.asm#L111-L113),
we could get rid of two permutations if either:
- We were able to express explicitly that we want to call at most one
operation in the current row, or
- We had an optimizer that would be smart enough to batch the memory
reads and writes.
- We could also have just 1 read or write at a time (instead of 2), but
we'd have to increase the block size from 31 to 32 and the
implementation would be more complicated.
- We could also store the full final state of the Poseidon permutation,
instead of just the first 4 elements. This would need 8 more witness
columns to make the entire output available in all rows. Then, one could
use the machine to implement a Poseidon sponge, instead of.
- Looking at the bootloader, maybe it makes sense to pass 3 input
pointers instead of 1: One for the first 4 elements, one for the next 4,
and one for the capacity (often just a constant). For example, when
computing a Merkle root, you'd pass pointers for the two children hashes
and a pointer to the capacity constant.
@lvella lvella self-assigned this Jul 8, 2024
github-merge-queue bot pushed a commit that referenced this pull request Jul 17, 2024
This PR contains the following changes:
- I renamed a bunch of functions `verify_*` to `run_pilcom_*`, because I
think it better describes what they do
- They all call `run_pilcom_with_backend_variant`, which works analogous
to `gen_estark_proof_with_backend_variant` and
`test_halo2_with_backend_variant`
- In functions like `run_pilcom_test_file` (previously
`verify_test_file`), which are used for many tests, we now test both the
monolithic and composite backend variant (but share the generated
witness & constants). This is anlogous to `gen_estark_proof` and
`test_halo2`
- In the RISC-V tests, we only test the composite variant, because with
registers in memory (#1443) we don't expect the monolithic backend
variant to work anymore.
Copy link
Collaborator

@georgwiese georgwiese left a comment

Choose a reason for hiding this comment

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

Cool! Looks pretty good, I think, comments to line changes ratio is low 😜

As a general comment, so we know how the number of rows change in typical programs? I think we do spend a few extra rows in some place, but should not be significant?

std/machines/memory.asm Show resolved Hide resolved
riscv-runtime/src/io.rs Outdated Show resolved Hide resolved
riscv/src/code_gen.rs Show resolved Hide resolved
riscv/src/code_gen.rs Outdated Show resolved Hide resolved
riscv/src/code_gen.rs Outdated Show resolved Hide resolved
riscv/src/runtime.rs Outdated Show resolved Hide resolved
riscv/src/continuations.rs Show resolved Hide resolved
Comment on lines +77 to +83
// TODO hacky way to find the degree of the main machine, fix.
let length = fixed_cols
.iter()
.find(|(col, _)| col == "main.STEP")
.unwrap()
.1
.len() as u64;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the correct way would be to include the length in the bootloader inputs.

Copy link
Member Author

Choose a reason for hiding this comment

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

next PR?

riscv/src/continuations.rs Outdated Show resolved Hide resolved
riscv/src/continuations.rs Outdated Show resolved Hide resolved
@leonardoalt leonardoalt changed the base branch from main to extract-rom July 22, 2024 10:52
Base automatically changed from extract-rom to main July 22, 2024 11:28
Copy link
Collaborator

@georgwiese georgwiese left a comment

Choose a reason for hiding this comment

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

LGTM

data_code.push(format!("mstore 0x{addr:x}, 0x{v:x};"));
data_code.push(format!(
"set_reg {}, 0x{v:x};",
Register::from("tmp2").addr()
Copy link
Member

Choose a reason for hiding this comment

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

May create a plain function fn reg_addr(&str) -> u32 ?

@georgwiese georgwiese added this pull request to the merge queue Jul 22, 2024
Merged via the queue into main with commit d8218a2 Jul 22, 2024
6 checks passed
@georgwiese georgwiese deleted the registers-in-memory-dev branch July 22, 2024 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Registers in Memory
5 participants