Note that in C, array types are not passed by value but this is by design: there are no array values in C although there are array types (you may need to repeat to yourself this last sentence several times before fully understanding it).
If our function is going to modify the parameter and we do not want to see the changes after the call, there is little that we can do. We have to invest some time in the parameter passing.
But what if our function does not actually modify the data? Or, what if we are interested in the changes the function did? Or even better, what if the parameter being modified is actually another output of the function?
Well, all these scenarios involve pointers.
15.4
Passing a big array by value
Consider an array of 32-bit integers and we want to sum all the elements. Our array will be in memory, it is just a contiguous sequence of 32-bit integers. We want to pass, somehow, the array to the function (together with the length of the array if the length may not be constant), sum all the integers and return the sum. Note that in this case the function does not modify the array, it just reads it.
Let’s define a function sum array value that must have the array of integers passed by value. The first parameter, in r0 will be the number of items of the integer array. Registers r1 to r3 may (or may not) have a value depending on the number of items in the array. So the first three elements must be handled differently. Then, if there are still items left, they must be loaded from the stack.
sum_array_value: /* We have passed all the data by value */ push {r4, r5, r6, lr}
/* r4 will hold the sum so far */ mov r4, #0 @ r4 <- 0
/* In r0 we have the number of items of the array */ cmp r0, #1 @ r0 - #1 and update cpsr
blt End_of_sum_array @ if r0 < 1 branch to End_of_sum_array add r4, r4, r1 @ add the first item
cmp r0, #2 @ r0 - #2 and update cpsr
blt End_of_sum_array @ if r0 < 2 branch to End_of_sum_array add r4, r4, r2 @ add the second item
15. Pointers
blt End_of_sum_array @ if r0 < 3 branch to End_of_sum_array add r4, r4, r3 @ add the third item
/*
The stack at this point looks like this
| | (higher addresses)
| |
| big_array[255] |
| ... |
| big_array[4] |
| big_array[3] | <- this is sp + 16 (we want r5 to point here) | r4 | <- this is sp + 12 | r5 | <- this is sp + 8 | r6 | <- this is sp + 4 | lr | <- sp points here | | | | (lower addresses)
keep in r5 the address where the stack-passed portion of the array starts
*/
add r5, sp, #16 @ r5 <- sp + 16
/* in register r3 we will count how many items we have read from the stack.
*/
mov r3, #0
/* in the stack there will always be up to 3 less items because the first 3 are already passed in registers
(recall that r0 had how many items were in the array) */
sub r0, r0, #3
b Check_loop_sum_array @ while loop with check first
Loop_sum_array:
ldr r6, [r5, r3, LSL #2] @ r6 <- *(r5 + r3 * 4) /* load the array item r3 from the stack */ add r4, r4, r6 @ r4 <- r4 + r6
15.4. Passing a big array by value
/* accumulate in r4 */ add r3, r3, #1 @ r3 <- r3 + 1
/* move to the next item */ Check_loop_sum_array:
cmp r3, r0 @ r0 - r3 and update cpsr */
blt Loop_sum_array @ if r3 < r0 branch to Loop_sum_array */
End_of_sum_array:
mov r0, r4 @ r0 <- r4, to return the value of the sum */ pop {r4, r5, r6, lr}
bx lr
The function is not particularly complex except for the special handling of the first 3 items (stored in r1 to r3) and that we have to be careful when locating elements of the array inside the stack. Upon entering the function the items of the array passed through the stack are laid out consecutively starting from sp. The push instruction at the beginning pushes onto the stack four registers (r4, r5, r6 and lr) so our array is now in sp + 16(see the diagram) and is eight byte aligned. Beside these details, we just loop through the items of the array and accumulate the sum in register r4. Finally, we move r4 into r0 for the return value of the function.
In order to call this function we have to put an array into the stack. Consider the following program. .data big_array: .word 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 .word 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 .word 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 .word 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69 .word 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86 .word 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102 .word 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115 .word 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127
message: .asciz "The sum of 0 to 127 is %d\n"
.text
.globl main
sum_array_value :
15. Pointers
main:
push {r4, r5, r6, r7, r8, lr}
/* we will not use r8 but we need to keep the function 8-byte aligned */
ldr r4, =big_array
/* Prepare call */
mov r0, #128 @ r0 <- 128
/* Load in the 1st parameter the number of items */
ldr r1, [r4] @ load in the 2nd parameter the 1st array item ldr r2, [r4, #4] @ load in the 3rd parameter the 2nd array item ldr r3, [r4, #8] @ load in the 4th parameter the 3rd array item
/* before pushing anything in the stack keep its position */ mov r7, sp
/* We cannot use more registers, now we have to push them onto the stack (in reverse order) */
mov r5, #127 @ r5 <- 127
/* This is the last item position
(note that the first would be in position 0) */
b Check_pass_parameter_loop @ while loop testing first
Pass_parameter_loop:
ldr r6, [r4, r5, LSL #2] @ r6 <- *(r4 + r5 * 4). /* loads the item in position r5 into r6.
Note that we have to multiply by 4 because that is the size of each item in the array */
push {r6} @ push the loaded value to the stack */ sub r5, r5, #1
/* we are done with the current item,
go to the previous index of the array */
Check_pass_parameter_loop:
cmp r5, #2 @ compute r5 - #2 and update cpsr bne Pass_parameter_loop @ if r5 != #2 => Pass_parameter_loop
/* We are done, we have passed all the values of the array, now call the function */