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
|
||||
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
|
||||
|
||||
|
@ -471,34 +473,49 @@ Here `var2` can't live in a register.
|
|||
|
||||
## Array operations
|
||||
|
||||
Mu arrays are size-prefixed so that operations on them can check bounds as
|
||||
necessary at run-time. The `length` statement returns the number of elements
|
||||
in an array.
|
||||
Here's an example definition of a fixed-length 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)
|
||||
```
|
||||
|
||||
The `index` statement takes an `addr` to an `array` and returns an `addr` to
|
||||
one of its elements, that can be read from or written to.
|
||||
To operate on elements of an array, use the `index` statement:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
has complex addressing modes that can index into an array in a single instruction
|
||||
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`.
|
||||
|
||||
```
|
||||
|
@ -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)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
| if size-of(T) is 4 or 8
|
||||
=> "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"
|
||||
var/reg <- index arr/rega: (addr array T), n
|
||||
=> "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"
|
||||
|
||||
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
|
||||
=> "(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 " " out " " size-of(T) ")"
|
||||
|
||||
|
|
Loading…
Reference in New Issue