7247
This commit is contained in:
parent
b6b94712a1
commit
2715d377b6
69
mu.md
69
mu.md
|
@ -45,7 +45,9 @@ will provide good error messages to support you.
|
||||||
|
|
||||||
Further down, this page enumerates all available primitives in Mu, and [a
|
Further down, this page enumerates all available primitives in Mu, and [a
|
||||||
separate page](http://akkartik.github.io/mu/html/mu_instructions.html)
|
separate page](http://akkartik.github.io/mu/html/mu_instructions.html)
|
||||||
describes how each primitive is translated to machine code.
|
describes how each primitive is translated to machine code. There is also a
|
||||||
|
useful list of pre-defined functions (implemented in unsafe machine code) in [400.mu](http://akkartik.github.io/mu/html/400.mu.html)
|
||||||
|
and [vocabulary.md](vocabulary.md).
|
||||||
|
|
||||||
## Functions and calls
|
## Functions and calls
|
||||||
|
|
||||||
|
@ -471,34 +473,49 @@ Here `var2` can't live in a register.
|
||||||
|
|
||||||
## Array operations
|
## Array operations
|
||||||
|
|
||||||
Mu arrays are size-prefixed so that operations on them can check bounds as
|
Here's an example definition of a fixed-length array:
|
||||||
necessary at run-time. The `length` statement returns the number of elements
|
|
||||||
in an array.
|
```
|
||||||
|
var x: (array int 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
The length (here `3`) must be an integer literal. We'll show how to create
|
||||||
|
dynamically-sized arrays further down.
|
||||||
|
|
||||||
|
Arrays can be large; to avoid copying them around on every function call
|
||||||
|
you'll usually want to manage `addr`s to them. Here's an example computing the
|
||||||
|
address of an array.
|
||||||
|
|
||||||
|
```
|
||||||
|
var n/eax: (addr array int) <- address x
|
||||||
|
```
|
||||||
|
|
||||||
|
Addresses to arrays don't include the array length in their type. However, you
|
||||||
|
can obtain the length of an array like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
var/reg: int <- length arr/reg: (addr array T)
|
var/reg: int <- length arr/reg: (addr array T)
|
||||||
```
|
```
|
||||||
|
|
||||||
The `index` statement takes an `addr` to an `array` and returns an `addr` to
|
To operate on elements of an array, use the `index` statement:
|
||||||
one of its elements, that can be read from or written to.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
var/reg: (addr T) <- index arr/reg: (addr array T), n
|
var/reg: (addr T) <- index arr/reg: (addr array T), n
|
||||||
var/reg: (addr T) <- index arr: (array T sz), n
|
var/reg: (addr T) <- index arr: (array T len), n
|
||||||
```
|
```
|
||||||
|
|
||||||
The index can also be a variable in a register, with a caveat:
|
The index can also be a variable in a register, with a caveat:
|
||||||
|
|
||||||
```
|
```
|
||||||
var/reg: (addr T) <- index arr/reg: (addr array T), idx/reg: int
|
var/reg: (addr T) <- index arr/reg: (addr array T), idx/reg: int
|
||||||
var/reg: (addr T) <- index arr: (array T sz), idx/reg: int
|
var/reg: (addr T) <- index arr: (array T len), idx/reg: int
|
||||||
```
|
```
|
||||||
|
|
||||||
The caveat: the size of T must be 1, 2, 4 or 8 bytes. The x86 instruction set
|
The caveat: the size of T must be 1, 2, 4 or 8 bytes. The x86 instruction set
|
||||||
has complex addressing modes that can index into an array in a single instruction
|
has complex addressing modes that can index into an array in a single instruction
|
||||||
in these situations.
|
in these situations.
|
||||||
|
|
||||||
For types in general you'll need to split up the work, performing a `compute-offset`
|
For other sizes of T you'll need to split up the work, performing a `compute-offset`
|
||||||
before the `index`.
|
before the `index`.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -514,6 +531,40 @@ performing any necessary bounds checking. Now the offset can be passed to
|
||||||
var/reg: (addr T) <- index arr/reg: (addr array T), idx/reg: (offset T)
|
var/reg: (addr T) <- index arr/reg: (addr array T), idx/reg: (offset T)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Stream operations
|
||||||
|
|
||||||
|
A common use for arrays is as buffers. Save a few items to a scratch space and
|
||||||
|
then process them. This pattern is so common (we use it in files) that there's
|
||||||
|
special support for it with a built-in type: `stream`.
|
||||||
|
|
||||||
|
Streams are like arrays in many ways. You can initialize them with a length:
|
||||||
|
|
||||||
|
```
|
||||||
|
var x: (stream int 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
However, streams don't provide random access with an `index` instruction.
|
||||||
|
Instead, you write to them sequentially, and read back what you wrote.
|
||||||
|
|
||||||
|
```
|
||||||
|
read-from-stream s: (addr stream T), out: (addr T)
|
||||||
|
write-to-stream s: (addr stream T), in: (addr T)
|
||||||
|
var/eax: boolean <- stream-empty? s: (addr stream)
|
||||||
|
var/eax: boolean <- stream-full? s: (addr stream)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can clear streams:
|
||||||
|
|
||||||
|
```
|
||||||
|
clear-stream f: (addr stream _)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also rewind them to reread what's been written:
|
||||||
|
|
||||||
|
```
|
||||||
|
rewind-stream f: (addr stream _)
|
||||||
|
```
|
||||||
|
|
||||||
## Compound types
|
## Compound types
|
||||||
|
|
||||||
Primitive types can be combined together using the `type` keyword. For
|
Primitive types can be combined together using the `type` keyword. For
|
||||||
|
|
|
@ -317,11 +317,11 @@ var/reg: (addr T) <- address var2: T
|
||||||
var/reg <- index arr/rega: (addr array T), idx/regi: int
|
var/reg <- index arr/rega: (addr array T), idx/regi: int
|
||||||
| if size-of(T) is 4 or 8
|
| if size-of(T) is 4 or 8
|
||||||
=> "8d/copy-address *(" rega "+" regi "<<" log2(size-of(T)) "+4) " reg "/r32"
|
=> "8d/copy-address *(" rega "+" regi "<<" log2(size-of(T)) "+4) " reg "/r32"
|
||||||
var/reg <- index arr: (array T sz), idx/regi: int
|
var/reg <- index arr: (array T len), idx/regi: int
|
||||||
=> "8d/copy-address *(ebp+" regi "<<" log2(size-of(T)) "+" (arr.stack-offset + 4) ") " reg "/r32"
|
=> "8d/copy-address *(ebp+" regi "<<" log2(size-of(T)) "+" (arr.stack-offset + 4) ") " reg "/r32"
|
||||||
var/reg <- index arr/rega: (addr array T), n
|
var/reg <- index arr/rega: (addr array T), n
|
||||||
=> "8d/copy-address *(" rega "+" (n*size-of(T)+4) ") " reg "/r32"
|
=> "8d/copy-address *(" rega "+" (n*size-of(T)+4) ") " reg "/r32"
|
||||||
var/reg <- index arr: (array T sz), n
|
var/reg <- index arr: (array T len), n
|
||||||
=> "8d/copy-address *(ebp+" (arr.stack-offset+4+n*size-of(T)) ") " reg "/r32"
|
=> "8d/copy-address *(ebp+" (arr.stack-offset+4+n*size-of(T)) ") " reg "/r32"
|
||||||
|
|
||||||
var/reg: (offset T) <- compute-offset arr: (addr array T), idx/regi: int # arr can be in reg or mem
|
var/reg: (offset T) <- compute-offset arr: (addr array T), idx/regi: int # arr can be in reg or mem
|
||||||
|
@ -382,6 +382,8 @@ populate in: (addr handle array T), num # can be literal or variable on stack o
|
||||||
populate-stream in: (addr handle stream T), num # can be literal or variable on stack or register
|
populate-stream in: (addr handle stream T), num # can be literal or variable on stack or register
|
||||||
=> "(new-stream Heap " size-of(T) " " num " " in ")"
|
=> "(new-stream Heap " size-of(T) " " num " " in ")"
|
||||||
|
|
||||||
|
# Some miscellaneous helpers to avoid error-prone size computations
|
||||||
|
|
||||||
read-from-stream s: (addr stream T), out: (addr T)
|
read-from-stream s: (addr stream T), out: (addr T)
|
||||||
=> "(read-from-stream " s " " out " " size-of(T) ")"
|
=> "(read-from-stream " s " " out " " size-of(T) ")"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue