diff --git a/section_1/funcs/README.md b/section_1/funcs/README.md index c9cb530..e10a143 100644 --- a/section_1/funcs/README.md +++ b/section_1/funcs/README.md @@ -4,7 +4,10 @@ Calling functions, passing parameters to them and receiving back return values is basic to using `C` and and `C++`. Calling methods (which are functions connected to objects) is similar but with enough differences to warrant its own discussion to be provided later in the chapter on -[structs](../struct/structs.md). +[structs](../structs/using.md). + +Be sure to read [this](./README2.md) for information about +passing parameters to functions. ## Bottom Line Concept @@ -62,8 +65,9 @@ allows the function to `ret`urn. ## **bl** Branch-with-link computes the address of the instruction following it. -It places this address into `x30` and then branches to the label -provided. It makes one link of breadcrumbs to follow to get back + +It places this address into register `x30` and then branches to the +label provided. It makes one link of breadcrumbs to follow to get back following a `ret`. **This is why it is absolutely essential to backup `x30` inside your @@ -72,23 +76,23 @@ functions if they call other functions themselves.** How about this trivial program: ```text - .text // 1 - .global main // 2 - .align 2 // 3 - // 4 -main: ldr x0, =hw // 5 - bl puts // 6 - ret // 7 - // 8 - .data // 9 -hw: .asciz "Hello World!" // 10 - // 11 - .end // 12 + .text // 1 + .global main // 2 + .align 2 // 3 + // 4 +main: ldr x0, =hw // 5 + bl puts // 6 + ret // 7 + // 8 + .data // 9 +hw: .asciz "Hello World!" // 10 + // 11 + .end // 12 ``` What could possibly go wrong? -Here is a listing from `gdb` since running the program simply +Here is a listing from `gdb` since running the program hangs: ```text @@ -128,31 +132,33 @@ return. So, when line 7 executes it puts the contents of `x30` into the program counter and branches to it. -And the problem with this is? Hint: notice where `gdb` put us after +And the problem with this is? + +Hint: notice where `gdb` put us after the control-C. Still on line 7. An infinite loop of returning to the return statement. Here is a fixed version of the code: ```text - .text // 1 - .global main // 2 - .align 2 // 3 - // 4 -main: str x30, [sp, -16]! // 5 - ldr x0, =hw // 6 - bl puts // 7 - ldr x30, [sp], 16 // 8 - ret // 9 - // 10 - .data // 11 -hw: .asciz "Hello World!" // 12 - // 13 - .end // 14 + .text // 1 + .global main // 2 + .align 2 // 3 + // 4 +main: str x30, [sp, -16]! // 5 + ldr x0, =hw // 6 + bl puts // 7 + ldr x30, [sp], 16 // 8 + ret // 9 + // 10 + .data // 11 +hw: .asciz "Hello World!" // 12 + // 13 + .end // 14 ``` The address to which `main()` should return is pushed onto the stack on -line 5. It is safe there. +line 5. It should be safe there. It is recovered from the stack on line 8 and used by line 9's `ret`. @@ -161,14 +167,16 @@ It is recovered from the stack on line 8 and used by line 9's `ret`. First, let's take a trip back in time to the early days of C. [Stephen Bourne](https://en.wikipedia.org/wiki/Stephen_R._Bourne) was -writing `sh` the first shell for Unix. He noticed that every function +writing `sh`, the first shell for Unix. He noticed that every function had to return a value - even functions that had no reason to return -a value. In these early days, `void` functions did not yet exist. +a value. -Bourne argued that an instruction could be saved if the concept of -`void` functions were added to C. Saving one instruction per function -was really valuable - so that's how we get `void` functions that -return no value. +In these early days, `void` functions did not yet exist. + +Bourne argued that an instruction could be saved per function if the +concept of `void` functions were added to C. Saving one instruction per +function was really valuable - so that's how we get `void` functions +that return no value. What about functions that do return a value? @@ -179,47 +187,52 @@ Note that `x0` and `x1` could also be `w0` and `w1` or even the first and second floating point registers if the function is returning a `float` or `double`. -Here are samples, first in C++ then in the corresponding assembly +Here are samples, first in C / C++ then in the corresponding assembly language: ```c++ - // 1 -int ReturnsAnInt() { // 2 - return 16; // 3 -} // 4 - // 5 -long ReturnsALong() { // 6 - return 16; // 7 -} // 8 - // 9 -float ReturnsAFloat() { // 10 - return 16.0f; // 11 -} // 12 - // 13 -double ReturnsADouble() { // 14 - return 16.0; // 15 -} // 16 + // 1 +int ReturnsAnInt() { // 2 + return 16; // 3 +} // 4 + // 5 +long ReturnsALong() { // 6 + return 16; // 7 +} // 8 + // 9 +float ReturnsAFloat() { // 10 + return 16.0f; // 11 +} // 12 + // 13 +double ReturnsADouble() { // 14 + return 16.0; // 15 +} // 16 ``` Here it is in assembly language: ```text -ReturnsAnInt: // 1 - mov w0, 16 // 2 - ret // 3 - // 4 -ReturnsALong: // 5 - mov x0, 16 // 6 - ret // 7 - // 8 -ReturnsAFloat: // 9 - fmov s0, 16 // 10 - ret // 11 - // 12 -ReturnsADouble: // 13 - fmov d0, 16 // 14 - ret // 15 +ReturnsAnInt: // 1 + mov w0, 16 // 2 + ret // 3 + // 4 +ReturnsALong: // 5 + mov x0, 16 // 6 + ret // 7 + // 8 +ReturnsAFloat: // 9 + fmov s0, 16 // 10 + ret // 11 + // 12 +ReturnsADouble: // 13 + fmov d0, 16 // 14 + ret // 15 ``` Note, the use of the floating point move instruction as well as the single precision and double precision registers. + +## Repeating the TL;DR + +If your functions call *any* other functions, `x30` must be backed +up on the stack and then restored into `x30` before returning. diff --git a/section_1/funcs/README2.md b/section_1/funcs/README2.md index 8f76c62..715c633 100644 --- a/section_1/funcs/README2.md +++ b/section_1/funcs/README2.md @@ -6,22 +6,22 @@ For the purposes of the present discussion, we assume all parameters are `long i Up to 8 parameters are passed in the scratch registers (of which there are 8). These are `x0` through `x7`. *Scratch* means the value of the register can be changed at will without any need to backup or restore their values. -**This also means that you cannot count on the contents of the scratch registers maintaining their value if your function makes any function calls itself.** +**This means that you cannot count on the contents of the scratch registers maintaining their value if your function makes any function calls.** For example: ```c -long func(long p1, long p2) // 1 -{ // 2 - return p1 + p2; // 3 -} // 4 +long func(long p1, long p2) // 1 +{ // 2 + return p1 + p2; // 3 +} // 4 ``` is implemented as: ```asm -func: add x0, x0, x1 // 1 - ret // 2 +func: add x0, x0, x1 // 1 + ret // 2 ``` The first parameter (`p1`) goes in the first scratch register (`x0`). It's an `x` because the parameter's type is `long int`. It is the `0` register, because that is the first scratch register. @@ -42,10 +42,10 @@ A pointer is an address of something. The word *pointer* is scary. The words *ad Here is a function which *also* adds two parameters together but this time using pointers to `long int` rather than the values themselves. ```c -void func(long * p1, long * p2) // 1 -{ // 2 - *p1 = *p1 + *p2; // 3 -} // 4 +void func(long * p1, long * p2) // 1 +{ // 2 + *p1 = *p1 + *p2; // 3 +} // 4 ``` `Line 1` passes the *address of* `p1` and `p2` as parameters. That is, the addresses of `p1` and `p2` are passed in registers `x0` and `x1` rather than their contents. The contents of the underlying @@ -60,11 +60,11 @@ The values are added together and the result overwrites the value pointed to by Here it is in assembly language: ```asm -func: ldr x2, [x0] // 1 - ldr x3, [x1] // 2 - add x2, x2, x3 // 3 - str x2, [x0] // 4 - ret // 5 +func: ldr x2, [x0] // 1 + ldr x3, [x1] // 2 + add x2, x2, x3 // 3 + str x2, [x0] // 4 + ret // 5 ``` The `add` instruction cannot operate on values in memory. With little exception, all the *action* takes place in registers, not memory. Therefore, the underlying values pointed to by the parameters must be fetched from memory. @@ -105,10 +105,10 @@ So, as the smart *human*, we decided to use `x2` and `x3` because, well, they're Suppose we had: ```c++ -long func(const long p1, const long p2) // 1 -{ // 2 - return p1 + p2; // 3 -} // 4 +long func(const long p1, const long p2) // 1 +{ // 2 + return p1 + p2; // 3 +} // 4 ``` how would the assembly language change? @@ -122,10 +122,10 @@ Answer: no change at all! Suppose we had: ```c++ -long func(long & p1, long & p2) // 1 -{ // 2 - return p1 + p2; // 3 -} // 4 +long func(long & p1, long & p2) // 1 +{ // 2 + return p1 + p2; // 3 +} // 4 ``` how would the assembly language change? @@ -150,9 +150,10 @@ Here is a sample function that requires 9 parameters (for who knows what reason) ```c++ #include -void SillyFunction(long p1, long p2, long p3, long p4, long p5, long p6, - long p7, long p8, long p9) { - printf("This example hurts my brain: %ld %ld\n", p8, p9); +void SillyFunction(long p1, long p2, long p3, long p4, + long p5, long p6, long p7, long p8, + long p9) { + printf("This example hurts: %ld %ld\n", p8, p9); } int main() { @@ -169,38 +170,38 @@ This example hurts my brain: 8 9 In assembly language, this program could be written as: ```text - .text // 1 - .global main // 2 - // 3 -SillyFunction: // 4 - str x30, [sp, -16]! // 5 - ldr x0, =fmt // 6 - mov x1, x7 // 7 - ldr x2, [sp, 16] // 8 - bl printf // 9 - ldr x30, [sp], 32 // 10 - ret // 11 - // 12 -main: // 13 - str x30, [sp, -16]! // 14 - mov x0, 9 // 15 - str x0, [sp, -16]! // 16 - mov x0, 1 // 17 - mov x1, 2 // 18 - mov x2, 3 // 19 - mov x3, 4 // 20 - mov x4, 5 // 21 - mov x5, 6 // 22 - mov x6, 7 // 23 - mov x7, 8 // 24 - bl SillyFunction // 25 - ldr x30, [sp], 32 // 26 - ret // 27 - // 28 - .data // 29 -fmt: .asciz "This example hurts my brain: %ld %ld\n" // 30 - // 31 - .end + .text // 1 + .global main // 2 + // 3 +SillyFunction: // 4 + str x30, [sp, -16]! // 5 + ldr x0, =fmt // 6 + mov x1, x7 // 7 + ldr x2, [sp, 16] // 8 + bl printf // 9 + ldr x30, [sp], 32 // 10 + ret // 11 + // 12 +main: // 13 + str x30, [sp, -16]! // 14 + mov x0, 9 // 15 + str x0, [sp, -16]! // 16 + mov x0, 1 // 17 + mov x1, 2 // 18 + mov x2, 3 // 19 + mov x3, 4 // 20 + mov x4, 5 // 21 + mov x5, 6 // 22 + mov x6, 7 // 23 + mov x7, 8 // 24 + bl SillyFunction // 25 + ldr x30, [sp], 32 // 26 + ret // 27 + // 28 + .data // 29 +fmt: .asciz "This example hurts: %ld %ld\n" // 30 + // 31 + ``` Notice how `main()` puts the first 8 parameters into the scratch registers `x0` through `x7` using `Lines 17` to `24`. But first, it put the ninth parameter on the stack. It did the stack parameter first so that the stack pointer could be manipulated in a scratch register.