Getting loops to work in Starknet's Cairo can be complicated. Currently you need to fiddle around a lot with ap
, fp
and pc
even for mundane computations.
Further, when calling a function within a loop tempvar
will be dereferenced causing the loop to fail. For example the following code will not run:
tempvar iterator=10
tempvar sum=0
loop_start:
let (local a, local b)=div (10, 5)
tempvar sum=sum+iterator
tempvar iterator=iterator-1
jmp loop_start if iterator != 0
A solution that allows calling function within loops, we save the variables that we want to save before the function call, with the [ap]= sum; ap++ command
. Then we call the modified function, which besides it's normal return values also returns my_fp
using the commands in the code.
With this fp
, we restore the saved variables, using tempvar sum=[my_fp-2-2-2]
. The -2-2-2 in general depends on the number of variables being saved, and the number of inputs the functions has. Eg. if we save 5 variables with [ap]
, and the add_one
function call had 12 inputs, then we would have to write [my_fp-12-2-5], ...,[my_fp-12-2-1]
respectively for the 5 restoration calls.
The fixed -2 is constant (from here). In general the formula is -#inputs-#saved_variables-2
.
This is ellucidated in the following snippet:
tempvar sum=0
tempvar iterator=10
loop_start:
[ap]=sum; ap++
[ap]=iterator; ap++
let (my_fp, a, b)=add_one (100, 50)
tempvar sum=[my_fp-4-2]
tempvar iterator=[my_fp-4-1]
tempvar sum=sum+a+b
tempvar iterator=iterator-1
jmp loop_start if iterator != 0
func add_one(x, y) -> (my_fp, q, r):
let fp_and_pc = get_fp_and_pc()
tempvar my_fp = fp_and_pc.fp_val
return (my_fp=my_fp,q=x+1, r=y+1)
end
With this trick (including fp
in the function return) the code remains readable and performant.