Skip to content

Commit 0d78d1e

Browse files
authored
refactor: rewrite Stack::push_slice to allow arbitrary lengths (#812)
1 parent 885c0cc commit 0d78d1e

File tree

2 files changed

+42
-53
lines changed

2 files changed

+42
-53
lines changed

crates/interpreter/src/instructions/stack.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ pub fn push0<H: Host, SPEC: Spec>(interpreter: &mut Interpreter<'_>, _host: &mut
2424

2525
pub fn push<const N: usize, H: Host>(interpreter: &mut Interpreter<'_>, _host: &mut H) {
2626
gas!(interpreter, gas::VERYLOW);
27-
let start = interpreter.instruction_pointer;
28-
// Safety: In Analysis we appended needed bytes for bytecode so that we are safe to just add without
29-
// checking if it is out of bound. This makes both of our unsafes block safe to do.
27+
// SAFETY: In analysis we append trailing bytes to the bytecode so that this is safe to do
28+
// without bounds checking.
29+
let ip = interpreter.instruction_pointer;
3030
if let Err(result) = interpreter
3131
.stack
32-
.push_slice::<N>(unsafe { core::slice::from_raw_parts(start, N) })
32+
.push_slice(unsafe { core::slice::from_raw_parts(ip, N) })
3333
{
3434
interpreter.instruction_result = result;
3535
return;
3636
}
37-
interpreter.instruction_pointer = unsafe { start.add(N) };
37+
interpreter.instruction_pointer = unsafe { ip.add(N) };
3838
}
3939

4040
pub fn dup<const N: usize, H: Host>(interpreter: &mut Interpreter<'_>, _host: &mut H) {

crates/interpreter/src/interpreter/stack.rs

+37-48
Original file line numberDiff line numberDiff line change
@@ -222,63 +222,52 @@ impl Stack {
222222
Ok(())
223223
}
224224

225-
/// Push a slice of bytes of `N` length onto the stack.
226-
///
227-
/// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack
228-
/// unchanged.
225+
/// Pushes an arbitrary length slice of bytes onto the stack, padding the last word with zeros
226+
/// if necessary.
229227
#[inline]
230-
pub fn push_slice<const N: usize>(&mut self, slice: &[u8]) -> Result<(), InstructionResult> {
231-
let new_len = self.data.len() + 1;
228+
pub fn push_slice(&mut self, slice: &[u8]) -> Result<(), InstructionResult> {
229+
if slice.is_empty() {
230+
return Ok(());
231+
}
232+
233+
let n_words = (slice.len() + 31) / 32;
234+
let new_len = self.data.len() + n_words;
232235
if new_len > STACK_LIMIT {
233236
return Err(InstructionResult::StackOverflow);
234237
}
235238

236-
let slot;
237-
// Safety: check above ensures us that we are okay in increment len.
239+
// SAFETY: length checked above.
238240
unsafe {
239-
self.data.set_len(new_len);
240-
slot = self.data.get_unchecked_mut(new_len - 1);
241-
}
241+
let dst = self.data.as_mut_ptr().add(self.data.len()).cast::<u64>();
242+
let mut i = 0;
242243

243-
unsafe {
244-
*slot.as_limbs_mut() = [0u64; 4];
245-
let mut dangling = [0u8; 8];
246-
if N < 8 {
247-
dangling[8 - N..].copy_from_slice(slice);
248-
slot.as_limbs_mut()[0] = u64::from_be_bytes(dangling);
249-
} else if N < 16 {
250-
slot.as_limbs_mut()[0] =
251-
u64::from_be_bytes(slice[N - 8..N].try_into().expect("Infallible"));
252-
if N != 8 {
253-
dangling[8 * 2 - N..].copy_from_slice(&slice[..N - 8]);
254-
slot.as_limbs_mut()[1] = u64::from_be_bytes(dangling);
255-
}
256-
} else if N < 24 {
257-
slot.as_limbs_mut()[0] =
258-
u64::from_be_bytes(slice[N - 8..N].try_into().expect("Infallible"));
259-
slot.as_limbs_mut()[1] =
260-
u64::from_be_bytes(slice[N - 16..N - 8].try_into().expect("Infallible"));
261-
if N != 16 {
262-
dangling[8 * 3 - N..].copy_from_slice(&slice[..N - 16]);
263-
slot.as_limbs_mut()[2] = u64::from_be_bytes(dangling);
264-
}
265-
} else {
266-
// M<32
267-
slot.as_limbs_mut()[0] =
268-
u64::from_be_bytes(slice[N - 8..N].try_into().expect("Infallible"));
269-
slot.as_limbs_mut()[1] =
270-
u64::from_be_bytes(slice[N - 16..N - 8].try_into().expect("Infallible"));
271-
slot.as_limbs_mut()[2] =
272-
u64::from_be_bytes(slice[N - 24..N - 16].try_into().expect("Infallible"));
273-
if N == 32 {
274-
slot.as_limbs_mut()[3] =
275-
u64::from_be_bytes(slice[..N - 24].try_into().expect("Infallible"));
276-
} else if N != 24 {
277-
dangling[8 * 4 - N..].copy_from_slice(&slice[..N - 24]);
278-
slot.as_limbs_mut()[3] = u64::from_be_bytes(dangling);
279-
}
244+
// write full words
245+
let limbs = slice.rchunks_exact(8);
246+
let rem = limbs.remainder();
247+
for limb in limbs {
248+
*dst.add(i) = u64::from_be_bytes(limb.try_into().unwrap());
249+
i += 1;
280250
}
251+
252+
// write remainder by padding with zeros
253+
if !rem.is_empty() {
254+
let mut tmp = [0u8; 8];
255+
tmp[8 - rem.len()..].copy_from_slice(rem);
256+
*dst.add(i) = u64::from_be_bytes(tmp);
257+
i += 1;
258+
}
259+
260+
debug_assert_eq!((i + 3) / 4, n_words, "wrote beyond end of stack");
261+
262+
// zero out upper bytes of last word
263+
let m = i % 4; // 32 / 8
264+
if m != 0 {
265+
dst.add(i).write_bytes(0, 4 - m);
266+
}
267+
268+
self.data.set_len(new_len);
281269
}
270+
282271
Ok(())
283272
}
284273

0 commit comments

Comments
 (0)