Skip to content

Commit c1ef659

Browse files
committed
feat: initial code for memcpy chip
1 parent f73da4d commit c1ef659

File tree

16 files changed

+2197
-86
lines changed

16 files changed

+2197
-86
lines changed

Cargo.lock

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ members = [
6161
"extensions/ecc/tests",
6262
"extensions/pairing/circuit",
6363
"extensions/pairing/guest",
64+
"extensions/memcpy/circuit",
65+
"extensions/memcpy/transpiler",
6466
"guest-libs/ff_derive/",
6567
"guest-libs/k256/",
6668
"guest-libs/p256/",

crates/circuits/primitives/src/assert_less_than/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use openvm_circuit_primitives_derive::AlignedBorrow;
33
use openvm_stark_backend::{
44
interaction::InteractionBuilder,
55
p3_air::AirBuilder,
6-
p3_field::{Field, FieldAlgebra},
6+
p3_field::{Field, FieldAlgebra, PrimeField32},
77
};
88

99
use crate::{
@@ -58,6 +58,14 @@ pub struct LessThanAuxCols<T, const AUX_LEN: usize> {
5858
pub lower_decomp: [T; AUX_LEN],
5959
}
6060

61+
impl<F: PrimeField32, const AUX_LEN: usize> Default for LessThanAuxCols<F, AUX_LEN> {
62+
fn default() -> Self {
63+
Self {
64+
lower_decomp: [F::ZERO; AUX_LEN],
65+
}
66+
}
67+
}
68+
6169
/// This is intended for use as a **SubAir**, not as a standalone Air.
6270
///
6371
/// This SubAir constrains that `x < y` when `count != 0`, assuming

crates/toolchain/openvm/src/memcpy.s

Lines changed: 4 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -252,30 +252,7 @@ memcpy:
252252
addi a3, a4, 16
253253
li a4, 16
254254
.LBBmemcpy0_9:
255-
lw a6, -12(a3)
256-
srli a5, a5, 24
257-
slli a7, a6, 8
258-
lw t0, -8(a3)
259-
or a5, a7, a5
260-
sw a5, 0(a1)
261-
srli a5, a6, 24
262-
slli a6, t0, 8
263-
lw a7, -4(a3)
264-
or a5, a6, a5
265-
sw a5, 4(a1)
266-
srli a6, t0, 24
267-
slli t0, a7, 8
268-
lw a5, 0(a3)
269-
or a6, t0, a6
270-
sw a6, 8(a1)
271-
srli a6, a7, 24
272-
slli a7, a5, 8
273-
or a6, a7, a6
274-
sw a6, 12(a1)
275-
addi a1, a1, 16
276-
addi a2, a2, -16
277-
addi a3, a3, 16
278-
bltu a4, a2, .LBBmemcpy0_9
255+
memcpy_loop 1
279256
addi a4, a3, -13
280257
j .LBBmemcpy0_25
281258
.LBBmemcpy0_11:
@@ -288,18 +265,7 @@ memcpy:
288265
bltu a2, a1, .LBBmemcpy0_15
289266
li a1, 15
290267
.LBBmemcpy0_14:
291-
lw a5, 0(a4)
292-
lw a6, 4(a4)
293-
lw a7, 8(a4)
294-
lw t0, 12(a4)
295-
sw a5, 0(a3)
296-
sw a6, 4(a3)
297-
sw a7, 8(a3)
298-
sw t0, 12(a3)
299-
addi a4, a4, 16
300-
addi a2, a2, -16
301-
addi a3, a3, 16
302-
bltu a1, a2, .LBBmemcpy0_14
268+
memcpy_loop 0
303269
.LBBmemcpy0_15:
304270
andi a1, a2, 8
305271
beqz a1, .LBBmemcpy0_17
@@ -325,30 +291,7 @@ memcpy:
325291
addi a3, a4, 16
326292
li a4, 18
327293
.LBBmemcpy0_20:
328-
lw a6, -12(a3)
329-
srli a5, a5, 8
330-
slli a7, a6, 24
331-
lw t0, -8(a3)
332-
or a5, a7, a5
333-
sw a5, 0(a1)
334-
srli a5, a6, 8
335-
slli a6, t0, 24
336-
lw a7, -4(a3)
337-
or a5, a6, a5
338-
sw a5, 4(a1)
339-
srli a6, t0, 8
340-
slli t0, a7, 24
341-
lw a5, 0(a3)
342-
or a6, t0, a6
343-
sw a6, 8(a1)
344-
srli a6, a7, 8
345-
slli a7, a5, 24
346-
or a6, a7, a6
347-
sw a6, 12(a1)
348-
addi a1, a1, 16
349-
addi a2, a2, -16
350-
addi a3, a3, 16
351-
bltu a4, a2, .LBBmemcpy0_20
294+
memcpy_loop 3
352295
addi a4, a3, -15
353296
j .LBBmemcpy0_25
354297
.LBBmemcpy0_22:
@@ -361,30 +304,7 @@ memcpy:
361304
addi a3, a4, 16
362305
li a4, 17
363306
.LBBmemcpy0_23:
364-
lw a6, -12(a3)
365-
srli a5, a5, 16
366-
slli a7, a6, 16
367-
lw t0, -8(a3)
368-
or a5, a7, a5
369-
sw a5, 0(a1)
370-
srli a5, a6, 16
371-
slli a6, t0, 16
372-
lw a7, -4(a3)
373-
or a5, a6, a5
374-
sw a5, 4(a1)
375-
srli a6, t0, 16
376-
slli t0, a7, 16
377-
lw a5, 0(a3)
378-
or a6, t0, a6
379-
sw a6, 8(a1)
380-
srli a6, a7, 16
381-
slli a7, a5, 16
382-
or a6, a7, a6
383-
sw a6, 12(a1)
384-
addi a1, a1, 16
385-
addi a2, a2, -16
386-
addi a3, a3, 16
387-
bltu a4, a2, .LBBmemcpy0_23
307+
memcpy_loop 2
388308
addi a4, a3, -14
389309
.LBBmemcpy0_25:
390310
mv a3, a1

crates/vm/src/system/memory/offline_checker/columns.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ impl<F: PrimeField32> MemoryBaseAuxCols<F> {
2626
}
2727
}
2828

29+
impl<F: PrimeField32> Default for MemoryBaseAuxCols<F> {
30+
fn default() -> Self {
31+
Self {
32+
prev_timestamp: F::ZERO,
33+
timestamp_lt_aux: LessThanAuxCols::default(),
34+
}
35+
}
36+
}
37+
2938
#[repr(C)]
3039
#[derive(Clone, Copy, Debug, AlignedBorrow)]
3140
pub struct MemoryWriteAuxCols<T, const N: usize> {
@@ -43,6 +52,11 @@ impl<const N: usize, T> MemoryWriteAuxCols<T, N> {
4352
self.base
4453
}
4554

55+
#[inline(always)]
56+
pub fn set_base(&mut self, base: MemoryBaseAuxCols<T>) {
57+
self.base = base;
58+
}
59+
4660
#[inline(always)]
4761
pub fn prev_data(&self) -> &[T; N] {
4862
&self.prev_data
@@ -80,6 +94,11 @@ impl<F: PrimeField32> MemoryReadAuxCols<F> {
8094
self.base
8195
}
8296

97+
#[inline(always)]
98+
pub fn set_base(&mut self, base: MemoryBaseAuxCols<F>) {
99+
self.base = base;
100+
}
101+
83102
/// Sets the previous timestamp **without** updating the less than auxiliary columns.
84103
#[inline(always)]
85104
pub fn set_prev(&mut self, timestamp: F) {

crates/vm/src/system/memory/offline_checker/mod.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,45 @@ mod columns;
55
pub use bridge::*;
66
pub use bus::*;
77
pub use columns::*;
8+
use openvm_circuit_primitives::is_less_than::LessThanAuxCols;
9+
use openvm_stark_backend::p3_field::PrimeField32;
810

911
#[repr(C)]
1012
#[derive(Debug, Clone)]
11-
pub struct MemoryReadAuxRecord {
13+
pub struct MemoryBaseAuxRecord {
1214
pub prev_timestamp: u32,
1315
}
1416

17+
#[repr(C)]
18+
#[derive(Debug, Clone)]
19+
pub struct MemoryExtendedAuxRecord {
20+
pub prev_timestamp: u32,
21+
pub timestamp_lt_aux: [u32; AUX_LEN],
22+
}
23+
24+
impl MemoryExtendedAuxRecord {
25+
pub fn from_aux_cols<F: PrimeField32>(aux_cols: MemoryBaseAuxCols<F>) -> Self {
26+
Self {
27+
prev_timestamp: aux_cols.prev_timestamp.as_canonical_u32(),
28+
timestamp_lt_aux: aux_cols
29+
.timestamp_lt_aux
30+
.lower_decomp
31+
.map(|x| x.as_canonical_u32()),
32+
}
33+
}
34+
35+
pub fn to_aux_cols<F: PrimeField32>(&self) -> MemoryBaseAuxCols<F> {
36+
MemoryBaseAuxCols {
37+
prev_timestamp: F::from_canonical_u32(self.prev_timestamp),
38+
timestamp_lt_aux: LessThanAuxCols {
39+
lower_decomp: self.timestamp_lt_aux.map(|x| F::from_canonical_u32(x)),
40+
},
41+
}
42+
}
43+
}
44+
45+
pub type MemoryReadAuxRecord = MemoryBaseAuxRecord;
46+
1547
#[repr(C)]
1648
#[derive(Debug, Clone)]
1749
pub struct MemoryWriteAuxRecord<T, const NUM_LIMBS: usize> {

extensions/memcpy/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# OpenVM Memcpy Extension
2+
3+
This extension provides a custom RISC-V instruction `memcpy_loop` that optimizes memory copy operations by handling different alignment shifts efficiently.
4+
5+
## Custom Instruction: `memcpy_loop shift`
6+
7+
### Format
8+
```
9+
memcpy_loop shift
10+
```
11+
12+
Where `shift` is an immediate value (0, 1, 2, or 3) representing the byte alignment shift.
13+
14+
### RISC-V Encoding
15+
- **Opcode**: `0x73` (custom opcode)
16+
- **Funct3**: `0x0` (custom funct3)
17+
- **Immediate**: 12-bit signed immediate for shift value
18+
- **Format**: I-type instruction
19+
20+
### Usage
21+
The `memcpy_loop` instruction is designed to replace repetitive shift-handling code in memcpy implementations. Instead of having separate code blocks for each shift value, you can use a single instruction:
22+
23+
```assembly
24+
# Instead of this repetitive code:
25+
.Lshift_1:
26+
lw a5, 0(a4)
27+
sb a5, 0(a3)
28+
srli a1, a5, 8
29+
sb a1, 1(a3)
30+
# ... more shift handling code
31+
32+
# You can use:
33+
memcpy_loop 1 # Handles shift=1 case
34+
```
35+
36+
### Benefits
37+
1. **Code Size Reduction**: Eliminates repetitive shift-handling code
38+
2. **Performance**: Optimized implementation in the circuit layer
39+
3. **Maintainability**: Single instruction handles all shift cases
40+
4. **Verification**: Zero-knowledge proof ensures correct execution
41+
42+
## Implementation Details
43+
44+
### Circuit Layer
45+
The instruction is implemented in the `MemcpyIterationAir` circuit which:
46+
- Reads 4 words (16 bytes) from memory
47+
- Applies the specified shift to combine words
48+
- Writes the result to the destination
49+
- Handles all shift values (0, 1, 2, 3) efficiently
50+
51+
### Transpiler Extension
52+
The `MemcpyTranspilerExtension` translates the RISC-V instruction into OpenVM's internal format:
53+
- Parses I-type instruction format
54+
- Validates shift value (0-3)
55+
- Converts to OpenVM instruction with shift as operand
56+
57+
### Example Usage
58+
See `example_memcpy_optimized.s` for a complete example showing how to use the custom instruction to optimize a memcpy implementation.
59+
60+
## Building and Testing
61+
62+
### Compilation
63+
```bash
64+
# Build the extension
65+
cargo build --package openvm-memcpy-circuit --package openvm-memcpy-transpiler
66+
67+
# Check for compilation errors
68+
cargo check --package openvm-memcpy-circuit --package openvm-memcpy-transpiler
69+
```
70+
71+
### Integration
72+
To use this extension in your OpenVM project:
73+
74+
1. Add the transpiler extension to your OpenVM configuration
75+
2. Use the `memcpy_loop` instruction in your RISC-V assembly
76+
3. The circuit will handle the execution and verification
77+
78+
## Architecture
79+
80+
```
81+
RISC-V Assembly → Transpiler Extension → OpenVM Instruction → MemcpyIterationAir → Execution
82+
```
83+
84+
The extension provides:
85+
- **Transpiler**: `extensions/memcpy/transpiler/` - Translates RISC-V to OpenVM
86+
- **Circuit**: `extensions/memcpy/circuit/` - Implements the instruction logic

extensions/memcpy/circuit/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "openvm-memcpy-circuit"
3+
description = "OpenVM circuit extension for memcpy"
4+
version.workspace = true
5+
authors.workspace = true
6+
edition.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
10+
[dependencies]
11+
openvm-circuit = { workspace = true }
12+
openvm-circuit-primitives = { workspace = true }
13+
openvm-circuit-primitives-derive = { workspace = true }
14+
openvm-circuit-derive = { workspace = true }
15+
openvm-instructions = { workspace = true }
16+
openvm-stark-backend = { workspace = true }
17+
openvm-memcpy-transpiler = { path = "../transpiler" }
18+
openvm-rv32im-transpiler = { workspace = true }
19+
openvm-rv32im-circuit = { workspace = true }
20+
21+
derive-new.workspace = true
22+
derive_more = { workspace = true, features = ["from"] }
23+
serde.workspace = true
24+
strum = { workspace = true }

0 commit comments

Comments
 (0)