Recursion lets a function call itself to solve smaller parts of a problem. In assembly, it helps break down complex tasks step-by-step.
Recursive function in assembly in ARM Architecture
/* ARM Assembly Recursive Function Template */ .global recursive_function recursive_function: push {lr} /* Save return address */ /* Base case check */ cmp r0, #0 /* Compare input parameter in r0 with 0 */ beq base_case /* If zero, jump to base case */ /* Recursive case */ sub r0, r0, #1 /* Decrement parameter */ bl recursive_function /* Recursive call */ /* Process after recursive call */ /* ... your code ... */ pop {pc} /* Return from function */ base_case: /* Base case code here */ mov r0, #1 /* Example: return 1 */ pop {pc} /* Return from function */
The function uses push {lr} to save the return address before calling itself.
The base case stops the recursion to avoid infinite calls.
/* Example: Factorial function in ARM assembly */ .global factorial factorial: push {lr} /* Save return address */ cmp r0, #0 /* Check if n == 0 */ beq factorial_base /* If yes, base case */ sub r0, r0, #1 /* n = n - 1 */ bl factorial /* Recursive call factorial(n-1) */ mul r0, r0, r1 /* Multiply result by n (stored in r1) */ pop {pc} /* Return */ factorial_base: mov r0, #1 /* factorial(0) = 1 */ pop {pc} /* Return */
/* Edge case: Recursive call with parameter 0 */ .global test_zero test_zero: push {lr} cmp r0, #0 beq base_case /* Recursive call if not zero */ sub r0, r0, #1 bl test_zero pop {pc} base_case: mov r0, #42 /* Return a fixed value for base case */ pop {pc}
/* Edge case: Recursive call with parameter 1 (single element) */ .global test_one test_one: push {lr} cmp r0, #1 beq base_case sub r0, r0, #1 bl test_one pop {pc} base_case: mov r0, #100 /* Return a fixed value for base case */ pop {pc}
/* Edge case: Recursive call at the beginning and end of a sequence */ .global recursive_sequence recursive_sequence: push {lr} cmp r0, #0 beq base_case cmp r0, #10 beq end_case sub r0, r0, #1 bl recursive_sequence pop {pc} base_case: mov r0, #0 pop {pc} end_case: mov r0, #10 pop {pc}
This program calculates factorial of 5 using recursion. It saves the return address, checks the base case (0), calls itself with n-1, then multiplies the result by n. The final result 120 is stored in register r0.
/* Complete ARM Assembly Program: Recursive Factorial Function */ .global main /* factorial function: calculates factorial of r0 */ factorial: push {lr} /* Save return address */ cmp r0, #0 /* Check if n == 0 */ beq factorial_base /* Base case: factorial(0) = 1 */ /* Save current n in r1 before recursive call */ mov r1, r0 sub r0, r0, #1 /* n = n - 1 */ bl factorial /* Recursive call factorial(n-1) */ mul r0, r0, r1 /* Multiply result by n */ pop {pc} /* Return */ factorial_base: mov r0, #1 /* Return 1 for factorial(0) */ pop {pc} /* main function to test factorial */ main: mov r0, #5 /* Calculate factorial(5) */ bl factorial /* Call factorial */ /* Result in r0 now contains 120 */ /* Normally, here you would return or use the result */ /* Infinite loop to end program (for embedded) */ end: b end
Time complexity of recursive factorial is O(n) because it calls itself n times.
Space complexity is O(n) due to call stack growth with each recursive call.
Common mistake: forgetting to save and restore the return address (lr) causes crashes.
Use recursion when problems naturally break down into smaller similar problems; otherwise, iteration may be simpler and more efficient.
Recursive functions call themselves to solve smaller parts of a problem.
In ARM assembly, save the return address with push {lr} before recursion and restore it with pop {pc} to return.
Always include a base case to stop recursion and avoid infinite loops.