I've been working on a project to write a recursive function in assembly, where it will calculate the Fibonacci number. To start off I made it in Java code:
public class Fibonacci {
public static int fibonacci(int n)
{
if(n <= 1)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
This recursive function worked perfectly fine. Although when trying to implement it in assembly code I didn't get the result I expected. After troubleshooting a while, I wrote (roughly) the equivalent code in Java:
static int n;
static int rec1;
static int result;
public static int asmFibonacci(int n)
{
if(n <= 1) {
result = n;
return 0;
}
n = n-1;
asmFibonacci(n);
rec1 = result;
n = n-1;
asmFibonacci(n);
result = rec1 + result;
return 0;
}
This function gets the same wrong result as the one I had in assembly code, although I still don't understand why, what am I missing? Both functions recur the same amount of times.
Results
asmFibonacci
0
1
1
2
2
3
3
4
4
5
Fibonacci
0
1
1
2
3
5
8
13
21
34
Any help would be much appreciated.
Update
After pushing rec1(R1) onto the stack as well, I got the Fibonacci subroutine in assembly to work as expected.
main
LDR R0, = 12
LDR R1, = 0
LDR R2, = 0
BL Fibonacci
STOP B STOP
Fibonacci
PUSH {LR, R0-R1}
CMP R0, #1
BLE RETURN
ADD R0, #-1
BL Fibonacci
MOV R1, R2
ADD R0, #-1
BL Fibonacci
ADD R0, R1, R2
RETURN
MOV R2, R0
POP {R0-R1, LR}
BX LR
END
Proper recursive code won't use static
storage; it's not re-entrant therefore not viable for recursion.
"re-entrant" means that it can be called while you're in the middle of another call, e.g. that evaluating Fib(3) while you're in the middle of Fib(5) doesn't mess up any variables that Fib(5) is going to want to re-read later. Such as static int rec1;
.
Use only local variables.
In asm, that means stack space or call-preserved registers. (Using call-preserved registers means preserving the caller's value, again on the stack).
Also note that static int n
is shadowed by your int n
function arg (the way you wrote it in Java at least), so you avoided the bug of trying to use static n
! static int rec2
is useless because you don't need to save it across anything.
Also, static int result;
is total insanity. Recursive functions return their result, not just produce a side-effect on a global / static var!
In asm, get used to using registers; not everything should get stored and reloaded from static storage with named labels. Even a debug-mode C compiler wouldn't do that (it would use stack space for locals)
Note that naive double-recursive Fibonacci only needs one total int-sized space for saving something across a call. Across the first call, you save n
. Across the second call, you need to save the result of the first call, but you're done with n
. You can recycle the same call-preserved register for that.
Recursive Fibonacci is total garbage for performance, of course, and only useful as an exercise in recursion. Approximately O(Fib(n)) vs. O(n) performance for simple iterative repeating x += y; SWAP(x,y)
or x+=y ; y+=x;
See Assembly Language (x86): How to create a loop to calculate Fibonacci sequence for efficient loops.