-
Notifications
You must be signed in to change notification settings - Fork 103
/
Copy pathgoldilocks.rs
111 lines (97 loc) · 3.12 KB
/
goldilocks.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use core::{arch::asm, mem::MaybeUninit};
use powdr_riscv_syscalls::Syscall;
pub const PRIME: u64 = 0xffffffff00000001;
/// Goldilocks field element.
///
/// Not a legal value in RISC-V, as it uses a supposedly
/// 32-bit memory word to store a full field element.
///
/// But it is more efficient when handled by Powdr machines.
///
/// TODO: remove the other Golilocks and the functions that use it,
/// and replace it with this one.
///
/// TODO: it might be necessary to do some Pin black magic to prevent
/// the compiler from trying to move this value using normal RISC-V
/// instructions.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct OpaqueGoldilocks(u32);
impl From<u32> for OpaqueGoldilocks {
fn from(value: u32) -> Self {
// This is a trivial conversion because the valid u32 values are
// a subset of the valid Goldilocks values.
Self(value)
}
}
impl From<Goldilocks> for OpaqueGoldilocks {
fn from(value: Goldilocks) -> Self {
let low = value.0 as u32;
let high = (value.0 >> 32) as u32;
unsafe {
let mut output = MaybeUninit::uninit();
ecall!(Syscall::MergeGL,
in("a0") low,
in("a1") high,
in("a2") output.as_mut_ptr());
output.assume_init()
}
}
}
/// Extract the Goldilocks values from the OpaqueGoldilocks values.
///
/// The array size must be a multiple of 4.
pub fn extract_opaque_vec<const N: usize>(vec: &[OpaqueGoldilocks; N]) -> [u64; N] {
assert_eq!(N % 4, 0);
unsafe {
let mut output: MaybeUninit<[u64; N]> = MaybeUninit::uninit();
let input_ptr = vec.as_ptr();
let output_ptr = (*output.as_mut_ptr()).as_mut_ptr();
for i in 0..(N / 4) {
ecall!(
Syscall::SplitGLVec,
in("a0") input_ptr.add(i * 4),
in("a1") output_ptr.add(i * 4)
);
}
output.assume_init()
}
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Goldilocks(
/// Canonical representation, only Goldilocks values are valid.
u64,
);
impl Goldilocks {
/// Panics if the value is not in the field (i.e. panics if value >= PRIME).
pub fn new(value: u64) -> Self {
assert!(value < PRIME);
Self(value)
}
/// Precondition for safety: value must be in the field (i.e. value < PRIME).
pub unsafe fn new_unchecked(value: u64) -> Self {
Self(value)
}
/// Returns the inverse of the field element.
pub fn inverse(self) -> Self {
let mut low = self.0 as u32;
let mut high = (self.0 >> 32) as u32;
unsafe {
ecall!(Syscall::InvertGL,
inout("a0") low,
inout("a1") high);
Self::new_unchecked((high as u64) << 32 | low as u64)
}
}
// TODO: maybe add other operations we can accelerate with ecalls:
// - add
// - mul
// - neg
}
impl From<Goldilocks> for u64 {
/// Return the canonical u64 representation of the field element.
fn from(value: Goldilocks) -> u64 {
value.0
}
}