Skip to content

Commit

Permalink
✨ Implement stack pop_n (#60)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

## Pull request type

<!-- Please try to limit your pull request to one type, submit multiple
pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying, or
link to a relevant issue. -->

Issue Number: N/A

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Pop N elements from the stack at once is now supported
  • Loading branch information
0xlny committed Oct 20, 2022
1 parent c655660 commit 170e316
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 3 deletions.
5 changes: 3 additions & 2 deletions src/kakarot/instructions/arithmetic_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ namespace ArithmeticOperations {
// 0 - a: first integer value to add.
// 1 - b: second integer value to add.
let stack = ctx.stack;
let (stack, a) = Stack.pop(stack);
let (stack, b) = Stack.pop(stack);
let (stack, len, popped) = Stack.pop_n(stack, 2);
let a = popped[0];
let b = popped[1];

// Compute the addition
let (result) = SafeUint256.add(a, b);
Expand Down
45 changes: 45 additions & 0 deletions src/kakarot/stack.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,36 @@ namespace Stack {
return (new_stack=new_stack, element=element);
}

// @notice Pop N elements from the stack.
// @param self - The pointer to the stack.
// @param len - The len of elements to pop.
// @return The new pointer to the stack.
// @return elements_len the popped elements.
// @return elements the pointer to the first popped element.
func pop_n{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
self: model.Stack*, len: felt
) -> (new_stack: model.Stack*, elements_len: felt, elements: Uint256*) {
alloc_locals;
Stack.check_underflow(self, len - 1);
// Get stack length
let current_len = Stack.len(self);
// Get new segment for next stack copy
let (new_elements: Uint256*) = alloc();
// Get length of new stack copy
let new_len = self.raw_len - (element_size * len);
// Copy stack without last N elements
memcpy(dst=new_elements, src=self.elements, len=new_len);

// Get new segment for popped elements copy
let (popped_elements: Uint256*) = alloc();
// Copy N last elements
copy_reverse(len, &self.elements[current_len - len], popped_elements);

// Create new stack
local new_stack: model.Stack* = new model.Stack(elements=new_elements, raw_len=new_len);
return (new_stack=new_stack, elements_len=len, elements=popped_elements);
}

// @notice Return a value from the stack at a given stack index.
// @dev stack_index is 0-based, 0 is the top of the stack.
// @param self - The pointer to the stack.
Expand Down Expand Up @@ -127,6 +157,21 @@ namespace Stack {
return Stack.push(dst_stack, top_value);
}

// @notice Copy & reverse a segment into dst.
// @param elements_len - The length of elements to copy.
// @param elements - The pointer to the source segment.
// @param dst - The pointer to the destination segment.
// @return The new pointer to the destination segment.
func copy_reverse{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
elements_len: felt, elements: Uint256*, dst: Uint256*
) -> (dst: Uint256*){
if(elements_len == 0){
return (dst=dst);
}
assert [dst] = elements[elements_len - 1];
return copy_reverse(elements_len - 1, elements, dst + element_size);
}

// @notice Copy a segment of the stack except at a given index.
// @dev stack_index is 0-based, 0 is the top of the stack.
// @param src_stack - The pointer to the source stack.
Expand Down
38 changes: 37 additions & 1 deletion tests/units/kakarot/test_stack.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,29 @@ func test__pop__should_pop_an_element_to_the_stack{
}

@external
func test__pop__should_fail__when_stack_underflow{
func test__pop__should_pop_N_elements_to_the_stack{
syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr
}() {
// Given
let stack: model.Stack* = Stack.init();
let stack: model.Stack* = Stack.push(stack, Uint256(1, 0));
let stack: model.Stack* = Stack.push(stack, Uint256(2, 0));
let stack: model.Stack* = Stack.push(stack, Uint256(3, 0));

// When
let (stack, elements_len, elements) = Stack.pop_n(stack, 3);

// Then
assert elements_len = 3;
assert elements[0] = Uint256(3, 0);
assert elements[1] = Uint256(2, 0);
assert elements[2] = Uint256(1, 0);
assert stack.raw_len = 0;
return ();
}

@external
func test__pop__should_fail__when_stack_underflow_pop{
syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr
}() {
// Given
Expand All @@ -94,6 +116,20 @@ func test__pop__should_fail__when_stack_underflow{
return ();
}

@external
func test__pop__should_fail__when_stack_underflow_pop_n{
syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr
}() {
// Given
let stack: model.Stack* = Stack.init();
let stack: model.Stack* = Stack.push(stack, Uint256(1, 0));

// When & Then
%{ expect_revert("TRANSACTION_FAILED", "Kakarot: StackUnderflow") %}
let (stack, elements_len, elements) = Stack.pop_n(stack, 2);
return ();
}

@external
func test__peek__should_return_stack_at_given_index__when_value_is_0{
syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr
Expand Down

0 comments on commit 170e316

Please sign in to comment.