task: primitive statements vs function calls
This commit is contained in:
parent
5713eb7302
commit
c7dde8d8b4
8
mu.md
8
mu.md
|
@ -7,7 +7,7 @@ Mu programs are sequences of `fn` and `type` definitions.
|
||||||
Define functions with the `fn` keyword. For example:
|
Define functions with the `fn` keyword. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn foo arg1: int, arg2: int -> result/eax: boolean
|
fn foo arg1: int, arg2: int -> _/eax: boolean
|
||||||
```
|
```
|
||||||
|
|
||||||
Functions contain `{}` blocks, `var` declarations, primitive statements and
|
Functions contain `{}` blocks, `var` declarations, primitive statements and
|
||||||
|
@ -36,6 +36,12 @@ Primitives can often write to arbitrary output registers. User-defined
|
||||||
functions, however, require rigidly specified output registers. Notice how the
|
functions, however, require rigidly specified output registers. Notice how the
|
||||||
definition of `foo` above writes to `eax`.
|
definition of `foo` above writes to `eax`.
|
||||||
|
|
||||||
|
A function's header names its inouts, but not its outputs (hence the `_`
|
||||||
|
above).
|
||||||
|
|
||||||
|
All Mu programs must define at least one function: `main`. That's where they
|
||||||
|
begin executing instructions.
|
||||||
|
|
||||||
## Variables, registers and memory
|
## Variables, registers and memory
|
||||||
|
|
||||||
Declare local variables in a function using the `var` keyword. As shown above,
|
Declare local variables in a function using the `var` keyword. As shown above,
|
||||||
|
|
|
@ -288,6 +288,72 @@ var x/edx: int <- copy 0
|
||||||
Run `translate` (or `translate_emulated`) as usual. Use your runbook from Task
|
Run `translate` (or `translate_emulated`) as usual. Use your runbook from Task
|
||||||
6 to address the errors that arise.
|
6 to address the errors that arise.
|
||||||
|
|
||||||
|
## Task 8: primitive statements vs function calls
|
||||||
|
|
||||||
|
Managing variables in memory vs register is one of two key skills to
|
||||||
|
programming in Mu. The second key skill is calling primitives (which are
|
||||||
|
provided by the x86 instruction set) vs functions (which are defined in terms
|
||||||
|
of primitives).
|
||||||
|
|
||||||
|
To prepare for this task, reread the very first section of the Mu reference,
|
||||||
|
on [functions and function calls](https://github.com/akkartik/mu/blob/main/mu.md#functions).
|
||||||
|
|
||||||
|
Now look at the following programs. In each case, write down whether you
|
||||||
|
expect translation to return any errors and why.
|
||||||
|
|
||||||
|
```
|
||||||
|
fn f a: int {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main {
|
||||||
|
f 0
|
||||||
|
var r/eax: int <- copy 3
|
||||||
|
f r
|
||||||
|
var m: int
|
||||||
|
f m
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(When you're ready, try the above program out as `./translate tutorial/task8a.mu`.)
|
||||||
|
|
||||||
|
```
|
||||||
|
fn f -> _/eax: int {
|
||||||
|
var result/ecx: int <- copy 0
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main {
|
||||||
|
var x/eax: int <- f
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(When you're ready, try the above program out as `./translate tutorial/task8b.mu`.)
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
fn f -> _/eax: int {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main {
|
||||||
|
var x/ecx: int <- f
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(When you're ready, try the above program out as `./translate tutorial/task8c.mu`.)
|
||||||
|
|
||||||
|
Functions have fewer restrictions than primitives on inouts, but more
|
||||||
|
restrictions on outputs. Inouts can be registers, or memory, or even literals.
|
||||||
|
This is why the first example above is legal. Outputs, however, _must_
|
||||||
|
hard-code specific registers, and function calls must write their outputs to
|
||||||
|
matching registers. This is why the third example above is illegal.
|
||||||
|
|
||||||
|
One subtlety here is that we only require agreement on output registers
|
||||||
|
between function call and function header. We don't actually have to `return`
|
||||||
|
the precise register a function header specifies. The return value can even be
|
||||||
|
a literal integer or in memory somewhere. The `return` is really just a `copy`
|
||||||
|
to the appropriate register(s). This is why the second example above is legal.
|
||||||
|
|
||||||
## Task 9: operating with fractional numbers
|
## Task 9: operating with fractional numbers
|
||||||
|
|
||||||
All our variables so far have had type `int` (integer), but there are limits
|
All our variables so far have had type `int` (integer), but there are limits
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
fn f a: int {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main {
|
||||||
|
f 0
|
||||||
|
var r/eax: int <- copy 3
|
||||||
|
f r
|
||||||
|
var m: int
|
||||||
|
f m
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fn f -> _/eax: int {
|
||||||
|
var result/ecx: int <- copy 0
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main {
|
||||||
|
var x/eax: int <- f
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn f -> _/eax: int {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main {
|
||||||
|
var x/ecx: int <- f
|
||||||
|
}
|
Loading…
Reference in New Issue