Part 19 – Linux ARM assembler function call stack uses – factorial

Call stack let’s take factorial as an example. Factorial is classical.

Stack definition: A stack is an area of memory owned only by the current dynamic activation.

Let’s first look at the C code for the next factorial as follows:

int factorial(int n)
{
   if (n == 0)
      return 1;
   else
      return n * factorial(n-1);
}
Copy the code

Factorial example 32 bits

.data message1: .asciz "Type a number: " format: .asciz "%d" message2: .asciz "The factorial of %d is %d\n" .text factorial: str lr, [sp,#-4]! /* Push lr to stack. */ str r0, [sp,#-4]! /* Push r0 to stack, #0 /* compare r0 and 0 */ bne is_nonzero /* if r0! R0 ← 1, function exit */ b end is_nonzero: /* Prepare the call to factorial(n-1) */ sub r0, r0, #1 /* r0 ← r0-1 */ bl factorial /* After the call r0 contains factorial(n-1) */ /* Load r0 (that we keep in th stack) Into R1 */ LDR R1, [sp] /* R1 ← * SP */ MUL R0, R1 /* R0 ← R0 * R1 */ end: Add sp, sp, #+4 /* discard r0 parameter */ LDR lr, [sp], #+4 /* load source LR into LR register */ bx lr /* exit factorial */. Global main main: str lr, [sp,#-4]! /* Save lr to stack */ sub sp, sp, #4 For user input save */ LDR r0, address_of_message1 /* Print mesg1*/ bl printf /* Call printf */ LDR r0, Address_of_format /* scanf format string argument */ mov R1, sp /* stack top as scanf second argument */ bl scanf /* call scanf */ LDR r0, [sp] /* Load the input argument to r0 */ bl factorial /* Call factorial */ mov R2, r0 /* the result is assigned to R2 as printf third argument */ LDR r1, [sp] /* the integer read in, As printf second argument */ LDR r0, address_of_message2 /* as printf first argument */ bl printf /* call printf */ add sp, sp, LDR lr, [sp], #+4 /* save lr*/ bx lr /* exit */ address_of_message1: .word message1 address_of_message2: .word message2 address_of_format: .word formatCopy the code

as -g -o fact.o fact.s

gcc -o fact fact.o

$./fact

Type a number: 9

The factorial of 9 is 362880

Optimize the code

LOAD Multiple instruction LDM and STORE Multiple instruction STM by loading/storing multiple data at once.

ldm addressing-mode Rbase{! }, register-set

stm addressing-mode Rbase{! }, the register set.

Block copy addressing enables continuous address data to be copied from one location in memory to another.

LDMIA/STMIA

LDMDA/STMDA

LDMIB/STMIB

LDMDB/STMDB

LDMIA R0! , {R1-R3}

Read three words from the storage cell in register R0 into registers R1-R3.

STMIA R0! , {R1-R3}

The contents stored in registers R1-R3 to R0 point to the Ed storage unit.

In LDMIA/LDMDA, I represents Increasing,D represents Decreasing,A represents After and B represents Before.

.data message1: .asciz "Type a number: " format: .asciz "%d" message2: .asciz "The factorial of %d is %d\n" .text factorial: stmdb sp! ,{r4,lr}// save r4,lr to sp execution stack, because db is subtracted first, then loaded. There are exclamation marks, so the last thing is to keep it to a minimum. Meets the requirements of the stack. mov r4,r0 cmp r0, #0 /* compare r0 and 0 */ bne is_nonzero /* if r0 ! = 0 then branch */ mov r0, #1 /* r0 ← 1. This is the return */ b end is_nonzero: /* Prepare the call to factorial(n-1) */ sub r0, r0, #1 /* R0 ← r0-1 */ bl factorial /* After the call r0 contains factorial(n-1) */ /* Load initial value of r0 (that we Kept in R4) into R1 */ mov R1, R4 /* R1 ← R4 */ MUL R0, R1 /* R0 ← R0 * R1 */ end: LDMIA sp! {r4,lr}// load the sp stack to r4 and LR, first load the value, then process the SP register, last sp is the last maximum value. Meets the requirements of the stack. bx lr /* Leave factorial */ .global main main: str lr, [sp,#-4]! /* Push lr onto the top of the stack */ sub sp, sp, #4 /* Make room for one 4 byte integer in the stack */ /* In these 4 bytes we will keep the number */ /* entered by the user */ /* Note that after that the stack is 8-byte aligned */ ldr r0, address_of_message1 /* Set &message1 as the first parameter of printf */ bl printf /* Call printf */ ldr r0, address_of_format /* Set &format as the first parameter of scanf */ mov r1, sp /* Set the top of the stack as the second parameter */ /* of scanf */ bl scanf /* Call scanf */ ldr r0, [sp] /* Load the integer read by scanf into r0 */ /* So we set it as the first parameter of factorial */ bl factorial /*  Call factorial */ mov r2, r0 /* Get the result of factorial and move it to r2 */ /* So we set it as the third parameter of printf */ ldr r1, [sp] /* Load the integer read by scanf into r1 */ /* So we set it as the second parameter of printf */ ldr r0, address_of_message2 /* Set &message2 as the first parameter of printf */ bl printf /* Call printf */ add sp, sp, #+4 /* Discard the integer read by scanf */ ldr lr, [sp], #+4 /* Pop the top of the stack and put it in lr */ bx lr /* Leave main */ address_of_message1: .word message1 address_of_message2: .word message2 address_of_format: .word formatCopy the code

as -g -o fact-o.o fact-o.s

gcc -o fact-o fact-o.o

$./fact-o

Type a number: 9

The factorial of 9 is 362880

 

Factorial example 64 bits

.data message1: .asciz "Type a number: " format: .asciz "%d" message2: .asciz "The factorial of %d is %d\n" .text .type factorial,@function .globl factorial factorial: stp x29, x30, [sp, -32]! STR x0, [sp,#-16] /* Push parameter r0 to stack */ CMP x0, #0 /* compare r0 and 0 */ bne is_nonzero /* if r0! R0 ← 1, function exit */ b end is_nonzero: /* Prepare the call to factorial(n-1) */ sub x0, x0, #1 /* r0 ← r0-1 */ bl factorial /* After the call r0 contains factorial(n-1) */ /* Load r0 (that we keep in th stack) Into R1 */ LDR x1, [sp,#-16] /* R1 ← *sp */ mul x0,x0,x1 end: LDP x29, x30, [sp], 32 ret. Global _start _start: Sub sp, sp, #16 /* Set aside a 4-byte space, For user input save */ LDR x0, address_of_message1 /* Print mesg1*/ bl printf /* Call printf */ LDR x0, Address_of_format /* scanf format string argument */ mov x1, sp /* stack top as scanf second argument */ bl scanf /* call scanf */ LDR x0, [sp] /* Load the input argument to r0 */ bl factorial /* Call factorial */ mov x2, x0 /* the result is assigned to R2 as printf third argument */ LDR x1, [sp] /* the integer read in, As printf second argument */ LDR x0, address_of_message2 /* as printf first argument */ bl printf /* call printf */ add sp, sp, SVC 0 address_of_message1:. Dword address_of_message2: .dword message2 address_of_format: .dword formatCopy the code

as -g -o fact64.o fact64.s

ld -g -o fact64 fact64.o  -lc -I /lib64/ld-linux-aarch64.so.1

The execution is complete.