better looking plus edits

This commit is contained in:
Perry Kivolowitz 2022-12-14 21:07:06 -06:00
parent bd1fe10183
commit 5c6fde83a1
2 changed files with 143 additions and 129 deletions

View file

@ -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 values is basic to using `C` and and `C++`. Calling methods (which are
functions connected to objects) is similar but with enough differences functions connected to objects) is similar but with enough differences
to warrant its own discussion to be provided later in the chapter on 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 ## Bottom Line Concept
@ -62,8 +65,9 @@ allows the function to `ret`urn.
## **bl** ## **bl**
Branch-with-link computes the address of the instruction following it. 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`. following a `ret`.
**This is why it is absolutely essential to backup `x30` inside your **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: How about this trivial program:
```text ```text
.text // 1 .text // 1
.global main // 2 .global main // 2
.align 2 // 3 .align 2 // 3
// 4 // 4
main: ldr x0, =hw // 5 main: ldr x0, =hw // 5
bl puts // 6 bl puts // 6
ret // 7 ret // 7
// 8 // 8
.data // 9 .data // 9
hw: .asciz "Hello World!" // 10 hw: .asciz "Hello World!" // 10
// 11 // 11
.end // 12 .end // 12
``` ```
What could possibly go wrong? 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: hangs:
```text ```text
@ -128,31 +132,33 @@ return.
So, when line 7 executes it puts the contents of `x30` into the So, when line 7 executes it puts the contents of `x30` into the
program counter and branches to it. 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 the control-C. Still on line 7. An infinite loop of returning to the
return statement. return statement.
Here is a fixed version of the code: Here is a fixed version of the code:
```text ```text
.text // 1 .text // 1
.global main // 2 .global main // 2
.align 2 // 3 .align 2 // 3
// 4 // 4
main: str x30, [sp, -16]! // 5 main: str x30, [sp, -16]! // 5
ldr x0, =hw // 6 ldr x0, =hw // 6
bl puts // 7 bl puts // 7
ldr x30, [sp], 16 // 8 ldr x30, [sp], 16 // 8
ret // 9 ret // 9
// 10 // 10
.data // 11 .data // 11
hw: .asciz "Hello World!" // 12 hw: .asciz "Hello World!" // 12
// 13 // 13
.end // 14 .end // 14
``` ```
The address to which `main()` should return is pushed onto the stack on 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`. 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. 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 [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 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 In these early days, `void` functions did not yet exist.
`void` functions were added to C. Saving one instruction per function
was really valuable - so that's how we get `void` functions that Bourne argued that an instruction could be saved per function if the
return no value. 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? 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 and second floating point registers if the function is returning a
`float` or `double`. `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: language:
```c++ ```c++
// 1 // 1
int ReturnsAnInt() { // 2 int ReturnsAnInt() { // 2
return 16; // 3 return 16; // 3
} // 4 } // 4
// 5 // 5
long ReturnsALong() { // 6 long ReturnsALong() { // 6
return 16; // 7 return 16; // 7
} // 8 } // 8
// 9 // 9
float ReturnsAFloat() { // 10 float ReturnsAFloat() { // 10
return 16.0f; // 11 return 16.0f; // 11
} // 12 } // 12
// 13 // 13
double ReturnsADouble() { // 14 double ReturnsADouble() { // 14
return 16.0; // 15 return 16.0; // 15
} // 16 } // 16
``` ```
Here it is in assembly language: Here it is in assembly language:
```text ```text
ReturnsAnInt: // 1 ReturnsAnInt: // 1
mov w0, 16 // 2 mov w0, 16 // 2
ret // 3 ret // 3
// 4 // 4
ReturnsALong: // 5 ReturnsALong: // 5
mov x0, 16 // 6 mov x0, 16 // 6
ret // 7 ret // 7
// 8 // 8
ReturnsAFloat: // 9 ReturnsAFloat: // 9
fmov s0, 16 // 10 fmov s0, 16 // 10
ret // 11 ret // 11
// 12 // 12
ReturnsADouble: // 13 ReturnsADouble: // 13
fmov d0, 16 // 14 fmov d0, 16 // 14
ret // 15 ret // 15
``` ```
Note, the use of the floating point move instruction as well as the Note, the use of the floating point move instruction as well as the
single precision and double precision registers. 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.

View file

@ -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. 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: For example:
```c ```c
long func(long p1, long p2) // 1 long func(long p1, long p2) // 1
{ // 2 { // 2
return p1 + p2; // 3 return p1 + p2; // 3
} // 4 } // 4
``` ```
is implemented as: is implemented as:
```asm ```asm
func: add x0, x0, x1 // 1 func: add x0, x0, x1 // 1
ret // 2 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. 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. Here is a function which *also* adds two parameters together but this time using pointers to `long int` rather than the values themselves.
```c ```c
void func(long * p1, long * p2) // 1 void func(long * p1, long * p2) // 1
{ // 2 { // 2
*p1 = *p1 + *p2; // 3 *p1 = *p1 + *p2; // 3
} // 4 } // 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 `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: Here it is in assembly language:
```asm ```asm
func: ldr x2, [x0] // 1 func: ldr x2, [x0] // 1
ldr x3, [x1] // 2 ldr x3, [x1] // 2
add x2, x2, x3 // 3 add x2, x2, x3 // 3
str x2, [x0] // 4 str x2, [x0] // 4
ret // 5 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. 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: Suppose we had:
```c++ ```c++
long func(const long p1, const long p2) // 1 long func(const long p1, const long p2) // 1
{ // 2 { // 2
return p1 + p2; // 3 return p1 + p2; // 3
} // 4 } // 4
``` ```
how would the assembly language change? how would the assembly language change?
@ -122,10 +122,10 @@ Answer: no change at all!
Suppose we had: Suppose we had:
```c++ ```c++
long func(long & p1, long & p2) // 1 long func(long & p1, long & p2) // 1
{ // 2 { // 2
return p1 + p2; // 3 return p1 + p2; // 3
} // 4 } // 4
``` ```
how would the assembly language change? 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++ ```c++
#include <stdio.h> #include <stdio.h>
void SillyFunction(long p1, long p2, long p3, long p4, long p5, long p6, void SillyFunction(long p1, long p2, long p3, long p4,
long p7, long p8, long p9) { long p5, long p6, long p7, long p8,
printf("This example hurts my brain: %ld %ld\n", p8, p9); long p9) {
printf("This example hurts: %ld %ld\n", p8, p9);
} }
int main() { int main() {
@ -169,38 +170,38 @@ This example hurts my brain: 8 9
In assembly language, this program could be written as: In assembly language, this program could be written as:
```text ```text
.text // 1 .text // 1
.global main // 2 .global main // 2
// 3 // 3
SillyFunction: // 4 SillyFunction: // 4
str x30, [sp, -16]! // 5 str x30, [sp, -16]! // 5
ldr x0, =fmt // 6 ldr x0, =fmt // 6
mov x1, x7 // 7 mov x1, x7 // 7
ldr x2, [sp, 16] // 8 ldr x2, [sp, 16] // 8
bl printf // 9 bl printf // 9
ldr x30, [sp], 32 // 10 ldr x30, [sp], 32 // 10
ret // 11 ret // 11
// 12 // 12
main: // 13 main: // 13
str x30, [sp, -16]! // 14 str x30, [sp, -16]! // 14
mov x0, 9 // 15 mov x0, 9 // 15
str x0, [sp, -16]! // 16 str x0, [sp, -16]! // 16
mov x0, 1 // 17 mov x0, 1 // 17
mov x1, 2 // 18 mov x1, 2 // 18
mov x2, 3 // 19 mov x2, 3 // 19
mov x3, 4 // 20 mov x3, 4 // 20
mov x4, 5 // 21 mov x4, 5 // 21
mov x5, 6 // 22 mov x5, 6 // 22
mov x6, 7 // 23 mov x6, 7 // 23
mov x7, 8 // 24 mov x7, 8 // 24
bl SillyFunction // 25 bl SillyFunction // 25
ldr x30, [sp], 32 // 26 ldr x30, [sp], 32 // 26
ret // 27 ret // 27
// 28 // 28
.data // 29 .data // 29
fmt: .asciz "This example hurts my brain: %ld %ld\n" // 30 fmt: .asciz "This example hurts: %ld %ld\n" // 30
// 31 // 31
.end
``` ```
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. 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.