Misty Programming Language:Misty Machine Code

Misty Machine

The Misty Machine is an abstract executor of Misty executables. It is used to define an intermediate representation that can be interpreted or fed to backend code generators for real machines.

When functions are called, an activation object is created containing slots that hold the arguments, variables, and temporary values of the invocation.

Memory

Each actor is given two blocks of memory. One is immutable, the other is mutable.

The immutable memory is where code objects are kept, as well as values that are stone and known at build time, such as numbers and texts.

The mutable memory is where the dynamic state of the actor is kept, including activation frames and data.

Misty Assembly Language

The format of Misty machine code, or MCODE, is JSON.

The instructions field contains an array containing labels and instructions.

Instructions describe a unit of work or flow. An instruction is encoded as an array containing up to four elements. The first element is the opcode represented as a text. The remaining zero thru three elements are operands. The names and meanings of the operands depend on the opcode.

Labels are the targets of branch instructions. Labels are encoded as simple text literals. Labels may be included for informing debuggers.

Slots are elements in an activation frame. They are designated by a small positive integer. Slots are the general registers of the misty machine. Slots hold the arguments, variables, and temporaries of a function invocation. The first three slots, 0, 1, and 2, are hardcoded with the values 0, 1, and null.

Intrinsics are represented by a token record.

{
    "name": "program_name",
    "data": {⸳⸳⸳},
    "source": "...",

The labels record associates labels with tokens for use by debuggers.

    "labels": {
        "entry": token,
        "beyond": token,
        ...
    },

The instructions array is a list of instructions and labels.

    instructions: [

A statement label:

        "entry",

go to beyond:

        ["branch", "beyond"],

assign slot 8: floor(pi / 2)

        ["access", 13, {"kind": "name", "text": "pi", "make": "intrinsic"}],
        ["floor", 14, 2],
        ["divide", 8, 13, 14],
        ⸳⸳⸳
        "beyond"
        ⸳⸳⸳
    ]
}

Misty Machine Instructions

Arithmetic

"add", dest, left, right

dest, left, and right are numbers designating slots in the current activation frame.

"subtract", dest, left, right

"multiply", dest, left, right

"divide", dest, left, right

"integer_divide", dest, left, right

"modulo", dest, left, right

"remainder", dest, left, right

"max", dest, left, right

"min", dest, left, right

"abs", dest, slot

"neg", dest, slot

"sign", dest, slot

"fraction", dest, slot

"integer", dest, slot

"neg", dest, slot

"ceiling", dest, slot, place

"floor", dest, slot, place

"round", dest, slot, place

"trunc", dest, slot, place

Text

"concat", dest, left, right

"concat_space", dest, left, right

"character", dest, slot

"codepoint", dest, slot

"length", dest, slot

"lower", dest, slot

"upper", dest, slot

"append", pretext, right

Append the right text to the pretext, forwarding and growing its capacity if necessary.

Comparison

These instructions compare the values in the left and right slots, and put a logical in the dest slot.

"eq", dest, left, right

"ne", dest, left, right

"lt", dest, left, right

"le", dest, left, right

"gt", dest, left, right

"ge", dest, left, right

Function

"frame", dest, func, nr_args

Prepare to invoke the func object. If the nr_args is too large, halt. Allocate the new activation frame. Put the current frame pointer into it.

"goframe", dest, func, nr_args

Same as frame, except that the current frame is reused if it is large enough.

"invoke", frame

Store the next instruction address in the current frame. Make the new frame the current frame. Jump to the entry point.

"goinvoke", frame

"apply", func, array

"return", value

"return_value", dest

Unconditional Branch

Ordinarily, control flows sequentially, from one instruction to the next. An unconditional branch alters the flow to another instruction. This is used to implement od, and else.

"branch", label

Conditional Branch

A conditional branch bifurcates the control flow. Control can flow to the labaelled instruction or to rhe next instruction, depending on the value in the slot.

Each of these instruction has a dual that flips the condition..

"branch_true", label, slot

If the value in the slot is true, branch to the label. Otherwise, continue with the next instruction.

"branch_not_true", label, slot

If the value in the slot is anything but true, branch to the label. Otherwise, continue with the next instruction.

"branch_false", label, slot

If the value in the slot is false, branch to the label. Otherwise, continue with the next instruction. This is different than branch_not_true because of the treatment of expressions that are neither true nor false.

"branch_not_false", label, slot

If the value in the slot is anything but false, branch to the label. Otherwise, continue with the next instruction.

"branch_null", label, slot

"branch_not_null", label, slot

"branch_empty", label, slot

"branch_not_empty", label, slot

"branch_zero", label, slot

"branch_not_zero", label, slot

Tranch

A tranch is a branch that can trifurcate the control flow.. The third alternative halts.

A tranch is used in place of a branch when it is not known for certain that the value in the slot is logical.

"tranch_true", label, slot

If the value in the slot is true, branch to the label. If the value is false, continue with the next instruction. Otherwise halt because of a type error.

"tranch_false", label, slot

If the value in the slot is false, branch to the label. If the value is true, continue with the next instruction. Otherwise halt because of a type error.

Sensory

Does the slot contain a value of the indicated type? These instructions put a logical in the dest slot.

"actor?", dest, slot

"array?", dest, slot

"blob?", dest, slot

"character?", dest, slot

"data?", dest, slot

"digit?", dest, slot

"false?", dest, slot

"fit?", dest, slot

"function?", dest, slot

"integer?", dest, slot

"letter?", dest, slot

"logical?", dest, slot

"null?", dest, slot

"pattern?", dest, slot

"record?", dest, slot

"stone?", dest, slot

"text?", dest, slot

"true?", dest, slot

"upper?", dest, slot

"whitespace?", dest, slot

Constants

"true", dest

"false", dest

"null", dest

"int", dest, small_int

Fetch

"access", dest, literal

This is used to access values (numbers, texts) from the program's immutable memory. The literal is a number or text.

"load", dest, object, subscript

This is used to load values from records and arrays.

"get", dest, slot, level

This is used to get values from slots in outer frames.

Mutate

"move", dest, slot

Move the value in the slot to a destination slot.

"store", dest, object, subscript

This is used to store values into records and arrays.

"put", value, slot, level

This is used to store values into slots in outer frames.

"push", array, slot

Append to a mutable array.

"pop", dest, array

Remove the last element of a mutable array, putting the element into dest.

Make

"array", dest, nr_elements

"blob", dest, nr_bits

"function", dest, code_name_text

"logical", dest, slot

"pretext", dest, nr_characters

A pretext must be converted to text by stone before it can leave the function scope.

"record", dest, nr_elements

"stone", dest, slot

Objects

An object is a data structure that sits at some address in the actor's memory.

Object Header
63 8 7 6 5 4 3 0
capacity r 00 s type
Object Type
code type
1 array
2 record
3 blob
4 text
5 function
6 pattern
7 actor address
12 frame
13 code
14 forward

Every object has a header word containing:

Array

  1. Header (type 1)
  2. Length
  3. Element[0]

The capacity is the number of elements that the array can hold. If more elements are needed, then the forward mechanism is used. During stoning or memory reclamation, the capacity is set to the length.

The length is the number of elements in use.

The elements follow, from [0] to [capacity - 1]

The number of words used by an array is capacity + 2.

Record

A record is an array of fields represented as key/value pairs. Fields are located by hashes of texts, using open addressing with linear probing and lazy deletion. The load factor is less than 0.5.

  1. Header (type 2)
  2. Length
  3. Key[1]
  4. Value[1]

The capacity is the number of fields the record can hold. It is a power of two minus one. It is at least twice the length.

The length is the number of fields that the record currently contains.

A field candidate number is identified by and(key.hash, capacity). In case of hash collision, advance to the next field. If this goes past the end, continue with field 1. Field 0 is reserved.

The number of words used by a record is (capacity + 1) * 2.

Blob

A blob is a binary large object, a container of bits.

  1. Header (type 3)
  2. Length
  3. Bit[0] thru Bit[63]

The capacity is the number of bits the blob can hold. If more bits are needed, then the forward mechanism is used. During stoning or memory reclamation, the capacity is set to the length.

The length is the number of elements in bits. The length is not contrained to be a multiple of a power of two.

The bits follow, from [0] to [capacity - 1], with the [0] bit in the most significant position of word 2, and [63] in the least significant position of word 2. The last word is zero filled, if necessary.

The number of words used by a blob is (capacity + 63) // 64 + 2.

Text

Text objects have two forms: mutable pretext, and immutable text, depending on the s flag.

Pretext is not a feature of the Misty language. It is a low level feature to support optimization of text operations.

  1. Header (type 4)
  2. Length (pretext) or Hash (text)
  3. Character[0] and Character[1]

The capacity of a pretext is the number of characters it can hold. If more characters are needed, then the forward mechanism is used. During stoning and memory reclamation, the capacity is set to the length.

The capacity of a text is its length, the number of characters it contains.

The length of a pretext is the number of characters it contains. This will not be greater than the capacity.

The hash of a text is used in organizing records. If the hash is zero, then the hash has not been computed yet. All texts in the immutable memory have hashes. Texts made by concat will not be given hashes until needed. The hash function is fash.

A text object contains a sequence of UTF32 characters, packed two per word, the first character in the higher order half. If the number of characters (length) is odd, then the least significant half of the last word is zero filled.

The number of words used by a text is (capacity + 1) // 2 + 2.

Function

  1. Header (type 5)
  2. Code
  3. Outer

A function object has a zero capacity and is always stone.

Code is a pointer to the code object that the function executes.

Outer is a pointer to the frame that created this function object.

The number of words used by a function object is 3.

Frame

  1. Header (type 12) # 0
  2. Continue address # 1
  3. Caller # null
  4. Function

The activation frame is created when a function is invoked to hold its linkages and state.

The capacity is the number of slots, including the arguments, variables, temporaries, and the four words of overhead. A frame, unlike the other types, is never stone.

The caller is the address of the frame that is invoking the function.

The continue address is the address of the instruction in the code that should be executed upon return.

The function slot is the address of the function object being called. It is used if the function is to call itself recursively.

Next come the arguments, if any.

Then the variables that are closed over by inner functions.

Then the variables that are not closed over, followed by the temporaries.

When a function returns, the caller is set to zero. This is a signal to the memory reclaimer that the frame can be reduced.

A program is not allowed to see or modify the values in slots 0, 1, and 2. A program is allow to see, but not modify slot 3.

Code

  1. Header (type 13)
  2. Arity
  3. Size
  4. Closure size
  5. Entry point

A code object exists in the actor's immutable memory. A code object never exists in mutable memory.

A code object has a zero capacity and is always stone.

The arity is the maximum number of arguments.

The size is the capacity of an activation frame that will execute this code.

The closure size is a reduced capacity for returned frames that survive memory reclamation.

The entry point is the address at which to begin execution.

Forward

  1. Header (type 14)

The forward type indicates that the object (an array, blob, pretext, or record) has grown beyond its capacity and is now residing at a new address. The 56 capacity bits contain the address of the enlarged object. Forward linkages are ultimately cleaned up by the memory reclaimer.