As you read through this section, be careful to distinguish data input/output to/from a called function from user input/output. User input typically comes from an input device (keyboard, mouse, etc.) and user output is typically sent to an output device (screen, printer, speaker, etc.).
Functions can interact with the data in other parts of the program in three ways:
Input: The data comes from another part of the program and is used by the function but is not modified by it.
Output: The function provides new data to another part of the program.
Update: The function modifies a data item that is held by another part of the program. The new value is based on the value before the function was called.
All three interactions can be performed if the called function also knows the location of the data item. This can be done by the calling function passing the address to the called function or by making the address globally known to both functions. Updates require that the address be known by the called function.
Outputs can also be implemented by placing the new data item in a location that is accessible to both the called and the calling function. In C/C++ this is done by placing the return value from a function in the r0 register. (For return values larger than 32 bits, registers r1–r3 are also used.) And inputs can be implemented by passing a copy of the data item to the called function. In both cases the called function does not know the location of the original data item, and thus does not have access to it.
In addition to global data, C syntax allows three ways for functions to exchange data:
- Pass By Value
An input value is passed by making a copy of it available to the function.
- Return Value
An output value can be returned to the calling function.
- Pass By Pointer
An output value can be stored for the calling function by passing the address where the output value should be stored to the called function. This can also be used to update a data item.
The last method, pass by pointer, can also be used to pass large inputs. It is also the method by which C++ implements pass by reference.
When one function calls another, the information that is required to provide the interface between the two is called an activation record. Since both the registers and the call stack are common to all the functions within a program, both the calling function and the called function have access to them. So arguments can be passed either in registers or on the call stack. Of course, the called function must know exactly where each of the arguments is located when program flow transfers to it.
In principle, the locations of arguments need only be consistent within a program. As long as all the programmers working on the program observe the same rules, everything should work. However, designing a good set of rules for any real-world project is a very time-consuming process. Fortunately, Procedure Call Standard for the ARM Architecture[A.1.3] (Procedure Call Standard for the ARM 64-bit Architecture[A.1.4] for 64-bit) specifies a good set of rules. The rules are very tedious because they are meant to cover all possible situations. In this book we will consider only the simpler rules in order to get an overall picture of how this works.