Skip to content

Commit 99e3802

Browse files
committed
signoff!: u32::shift_left and u32::shift_right
BREAKING CHANGE: Remove the implementations of `DeprecatedSnippet` for `u32::shift_left::ShiftLeft` and `u32::shift_right::ShiftRight`, implement `BasicSnippet` directly.
1 parent 1db9047 commit 99e3802

File tree

5 files changed

+176
-250
lines changed

5 files changed

+176
-250
lines changed

tasm-lib/benchmarks/tasmlib_arithmetic_i128_shift_right.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
{
33
"name": "tasmlib_arithmetic_i128_shift_right",
44
"benchmark_result": {
5-
"clock_cycle_count": 250,
5+
"clock_cycle_count": 245,
66
"hash_table_height": 120,
77
"u32_table_height": 290,
8-
"op_stack_table_height": 189,
8+
"op_stack_table_height": 179,
99
"ram_table_height": 0
1010
},
1111
"case": "CommonCase"
1212
},
1313
{
1414
"name": "tasmlib_arithmetic_i128_shift_right",
1515
"benchmark_result": {
16-
"clock_cycle_count": 236,
16+
"clock_cycle_count": 231,
1717
"hash_table_height": 120,
1818
"u32_table_height": 310,
19-
"op_stack_table_height": 181,
19+
"op_stack_table_height": 171,
2020
"ram_table_height": 0
2121
},
2222
"case": "WorstCase"

tasm-lib/benchmarks/tasmlib_arithmetic_u32_shift_right.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
{
33
"name": "tasmlib_arithmetic_u32_shift_right",
44
"benchmark_result": {
5-
"clock_cycle_count": 16,
5+
"clock_cycle_count": 15,
66
"hash_table_height": 18,
77
"u32_table_height": 46,
8-
"op_stack_table_height": 13,
8+
"op_stack_table_height": 11,
99
"ram_table_height": 0
1010
},
1111
"case": "CommonCase"
1212
},
1313
{
1414
"name": "tasmlib_arithmetic_u32_shift_right",
1515
"benchmark_result": {
16-
"clock_cycle_count": 16,
16+
"clock_cycle_count": 15,
1717
"hash_table_height": 18,
1818
"u32_table_height": 46,
19-
"op_stack_table_height": 13,
19+
"op_stack_table_height": 11,
2020
"ram_table_height": 0
2121
},
2222
"case": "WorstCase"
Lines changed: 82 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,177 +1,136 @@
11
use std::collections::HashMap;
22

3-
use rand::prelude::*;
43
use triton_vm::prelude::*;
54

6-
use crate::empty_stack;
5+
use crate::arithmetic::u32::shift_right::ShiftRight;
76
use crate::prelude::*;
8-
use crate::traits::deprecated_snippet::DeprecatedSnippet;
9-
use crate::InitVmState;
10-
11-
#[derive(Clone, Debug)]
7+
use crate::traits::basic_snippet::Reviewer;
8+
use crate::traits::basic_snippet::SignOffFingerprint;
9+
10+
/// [Shift left][shl] for unsigned 32-bit integers.
11+
///
12+
/// # Behavior
13+
///
14+
/// ```text
15+
/// BEFORE: _ [arg: u32] shift_amount
16+
/// AFTER: _ [result: u32]
17+
/// ```
18+
///
19+
/// # Preconditions
20+
///
21+
/// - input argument `arg` is properly [`BFieldCodec`] encoded
22+
/// - input argument `shift_amount` is in `0..32`
23+
///
24+
/// # Postconditions
25+
///
26+
/// - the output is the input argument `arg` bit-shifted to the left by
27+
/// input argument `shift_amount`
28+
/// - the output is properly [`BFieldCodec`] encoded
29+
///
30+
/// [shl]: core::ops::Shl
31+
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
1232
pub struct ShiftLeft;
1333

14-
impl DeprecatedSnippet for ShiftLeft {
15-
fn entrypoint_name(&self) -> String {
16-
"tasmlib_arithmetic_u32_shift_left".to_string()
17-
}
18-
19-
fn input_field_names(&self) -> Vec<String> {
20-
vec!["value".to_string(), "shift".to_string()]
21-
}
22-
23-
fn input_types(&self) -> Vec<DataType> {
24-
vec![DataType::U32, DataType::U32]
25-
}
34+
impl ShiftLeft {
35+
pub const SHIFT_AMOUNT_TOO_BIG_ERROR_ID: i128 = 480;
36+
}
2637

27-
fn output_field_names(&self) -> Vec<String> {
28-
vec!["value << shift".to_string()]
38+
impl BasicSnippet for ShiftLeft {
39+
fn inputs(&self) -> Vec<(DataType, String)> {
40+
ShiftRight.inputs()
2941
}
3042

31-
fn output_types(&self) -> Vec<DataType> {
32-
vec![DataType::U32]
43+
fn outputs(&self) -> Vec<(DataType, String)> {
44+
ShiftRight.outputs()
3345
}
3446

35-
fn stack_diff(&self) -> isize {
36-
-1
47+
fn entrypoint(&self) -> String {
48+
"tasmlib_arithmetic_u32_shift_left".to_string()
3749
}
3850

39-
fn function_code(&self, _library: &mut Library) -> String {
40-
let entrypoint = self.entrypoint_name();
41-
42-
// I'm unsure if we should do a bounds check to check if `shift < 32`
43-
format!(
44-
"
51+
fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
52+
triton_asm!(
4553
// BEFORE: _ value shift
4654
// AFTER: _ (value << shift)
47-
{entrypoint}:
48-
// Bounds check. May be superfluous but this mimics Rust's behavior.
55+
{self.entrypoint()}:
56+
/* bounds check mimics Rust's behavior */
4957
push 32
5058
dup 1
5159
lt
52-
assert
60+
assert error_id {Self::SHIFT_AMOUNT_TOO_BIG_ERROR_ID}
5361

5462
push 2
5563
pow
5664
mul
5765
split
58-
swap 1
66+
pick 1
5967
pop 1
6068

6169
return
62-
"
6370
)
6471
}
6572

66-
fn crash_conditions(&self) -> Vec<String> {
67-
vec![
68-
"inputs are not valid u32s".to_string(),
69-
"attempting to left shift with a value greater than 31".to_string(),
70-
]
71-
}
72-
73-
fn gen_input_states(&self) -> Vec<InitVmState> {
74-
let mut rng = thread_rng();
75-
let mut ret: Vec<InitVmState> = vec![];
76-
for _ in 0..100 {
77-
let value = rng.next_u32();
78-
let shift = rng.gen_range(0..32);
79-
ret.push(prepare_state(value, shift));
80-
}
81-
82-
ret
83-
}
84-
85-
fn common_case_input_state(&self) -> InitVmState {
86-
prepare_state((1 << 16) - 1, 16)
87-
}
88-
89-
fn worst_case_input_state(&self) -> InitVmState {
90-
prepare_state(u32::MAX, 31)
91-
}
92-
93-
fn rust_shadowing(
94-
&self,
95-
stack: &mut Vec<BFieldElement>,
96-
_std_in: Vec<BFieldElement>,
97-
_secret_in: Vec<BFieldElement>,
98-
_memory: &mut HashMap<BFieldElement, BFieldElement>,
99-
) {
100-
// Find shift amount
101-
let shift_amount: u32 = stack.pop().unwrap().try_into().unwrap();
102-
103-
// Original value
104-
let value: u32 = stack.pop().unwrap().try_into().unwrap();
105-
106-
let ret = value << shift_amount;
107-
stack.push((ret as u64).into());
73+
fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
74+
let mut sign_offs = HashMap::new();
75+
sign_offs.insert(Reviewer("ferdinand"), 0xec49012951c99eb1.into());
76+
sign_offs
10877
}
10978
}
11079

111-
fn prepare_state(value: u32, shift: u32) -> InitVmState {
112-
let mut stack = empty_stack();
113-
let value = BFieldElement::new(value as u64);
114-
let shift = BFieldElement::new(shift as u64);
115-
stack.push(value);
116-
stack.push(shift);
117-
118-
InitVmState::with_stack(stack)
119-
}
120-
12180
#[cfg(test)]
12281
mod tests {
12382
use super::*;
124-
use crate::test_helpers::test_rust_equivalence_given_input_values_deprecated;
125-
use crate::test_helpers::test_rust_equivalence_multiple_deprecated;
83+
use crate::test_prelude::*;
12684

127-
#[test]
128-
fn shift_left_test() {
129-
test_rust_equivalence_multiple_deprecated(&ShiftLeft, true);
130-
}
85+
impl Closure for ShiftLeft {
86+
type Args = (u32, u32);
13187

132-
#[test]
133-
fn shift_left_max_value_test() {
134-
for i in 0..32 {
135-
prop_shift_left(u32::MAX, i);
88+
fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
89+
let (arg, shift_amount) = pop_encodable::<Self::Args>(stack);
90+
assert!(shift_amount < 32);
91+
push_encodable(stack, &(arg << shift_amount));
92+
}
93+
94+
fn pseudorandom_args(
95+
&self,
96+
seed: [u8; 32],
97+
bench_case: Option<BenchmarkCase>,
98+
) -> Self::Args {
99+
let mut rng = StdRng::from_seed(seed);
100+
match bench_case {
101+
Some(BenchmarkCase::CommonCase) => ((1 << 16) - 1, 16),
102+
Some(BenchmarkCase::WorstCase) => (u32::MAX, 31),
103+
None => (rng.gen(), rng.gen_range(0..32)),
104+
}
105+
}
106+
107+
fn corner_case_args(&self) -> Vec<Self::Args> {
108+
(0..32).map(|i| (u32::MAX, i)).collect()
136109
}
137110
}
138111

139112
#[test]
140-
#[should_panic]
141-
fn shift_beyond_limit() {
142-
let mut init_stack = empty_stack();
143-
init_stack.push(BFieldElement::new(u32::MAX as u64));
144-
init_stack.push(32u64.into());
145-
ShiftLeft.link_and_run_tasm_from_state_for_test(&mut InitVmState::with_stack(init_stack));
113+
fn rust_shadow() {
114+
ShadowedClosure::new(ShiftLeft).test();
146115
}
147116

148-
fn prop_shift_left(value: u32, shift_amount: u32) {
149-
let mut init_stack = empty_stack();
150-
init_stack.push(BFieldElement::new(value as u64));
151-
init_stack.push(BFieldElement::new(shift_amount as u64));
152-
153-
let expected_u32 = value << shift_amount;
154-
155-
let mut expected_stack = empty_stack();
156-
expected_stack.push((expected_u32 as u64).into());
157-
158-
test_rust_equivalence_given_input_values_deprecated(
159-
&ShiftLeft,
160-
&init_stack,
161-
&[],
162-
HashMap::default(),
163-
Some(&expected_stack),
164-
);
117+
#[proptest]
118+
fn too_big_shift_amount_crashes_vm(arg: u32, #[strategy(32_u32..)] shift_amount: u32) {
119+
test_assertion_failure(
120+
&ShadowedClosure::new(ShiftLeft),
121+
InitVmState::with_stack(ShiftLeft.set_up_test_stack((arg, shift_amount))),
122+
&[ShiftLeft::SHIFT_AMOUNT_TOO_BIG_ERROR_ID],
123+
)
165124
}
166125
}
167126

168127
#[cfg(test)]
169128
mod benches {
170129
use super::*;
171-
use crate::snippet_bencher::bench_and_write;
130+
use crate::test_prelude::*;
172131

173132
#[test]
174-
fn shift_left_benchmark() {
175-
bench_and_write(ShiftLeft);
133+
fn benchmark() {
134+
ShadowedClosure::new(ShiftLeft).bench();
176135
}
177136
}

0 commit comments

Comments
 (0)