Skip to content

Latest commit

 

History

History
1814 lines (1474 loc) · 74.4 KB

vm.md

File metadata and controls

1814 lines (1474 loc) · 74.4 KB

Virtual Machine Semantics

Contents

Introduction

The uFork virtual machine is designed to support machine-level actors. All instructions execute within the context of an actor handling a message-event. There is no support for address arithmetic or load/store of arbitrary memory. Mutation is always local to an actor's private state. Immutable values are passed between actors via message-events. External events (such as "interrupts") are turned into message-events.

Representation

The quad-cell is the primary internal data-structure in uFork. It consists of four unsigned integers (the current WASM target uses 32-bit words).

T X Y Z
type/proc head/car tail/cdr link/next

The integers in each field carry a type-tag in their 3 most-significant bits (MSBs). The 1st MSB is {0=indirect-reference, 1=direct-value}. The 2nd MSB is {0=immutable, 1=mutable}. The 3rd MSB is {0=transparent, 1=opaque}. The resulting type-heirarchy looks like this:

                   any-value
                  0 /     \ 1
        (ptr) indirect   direct (fixnum)
             0 /    \ 1
  (rom) immutable  mutable
                  0 /   \ 1
     (ram) transparent opaque (ocap)

Direct values (fixnums) are stored in 2's-complement representation, where the 2nd MSB is the sign bit of the integer value.

Indirect values (pointers) designate quad-cells (with fields [T, X, Y, Z]).

Mutable values designate a quad that may be written as well as read. Since actor-state is mutable, the quad representing the actor must stored in writable memory.

Opaque values (object-capabilities) cannot be dereferenced except by the virtual-processor (to implement actor primitive operations).

The machine-code semantics provide no way to convert between fixnums, ocaps, and quad-cell pointers.

Data Structures

Quad-cells are used to encode most of the important data-structures in uFork.

Structure Description
[sponsor, target, msg, next] message-event queue entry
[IP, SP, EP, next] continuation queue entry
[#instr_t, opcode, data, next] machine instruction (typical)
[#pair_t, head, tail, #?] pair-lists of user data (cons)
[#pair_t, item, rest, #?] stack entry holding item
[#actor_t, beh, sp, #?] idle actor
[#actor_t, beh, sp, effects] busy actor
[#actor_t, beh', sp', events] effects, initial events=()
[#dict_t, key, value, next] dictionary binding entry
[FREE_T, #?, #?, next] cell in the free-list

Reserved ROM

Name Address T X Y Z Description
#? ^00000000 #? #? #? #? Undefined
() ^00000001 #? #? #? #? Nil (empty list)
#f ^00000002 #? #? #? #? Boolean False
#t ^00000003 #? #? #? #? Boolean True
#unit ^00000004 #? #? #? #? Unit (inert)
EMPTY_DQ ^00000005 #pair_t () () #? Empty Deque
#type_t ^00000006 #type_t +1 #? #? Type of Types
#fixnum_t ^00000007 #type_t #? #? #? Fixnum Type
#actor_t ^00000008 #type_t +2 #? #? Actor (ocap) Type
PROXY_T ^00000009 #type_t +2 #? #? Proxy Type
STUB_T ^0000000A #type_t +2 #? #? Stub Type
#instr_t ^0000000B #type_t +3 #? #? Instruction Type
#pair_t ^0000000C #type_t +2 #? #? Pair Type
#dict_t ^0000000D #type_t +3 #? #? Dictionary Type
FWD_REF_T ^0000000E #type_t -1 #? #? GC Fwd-Ref Type
FREE_T ^0000000F #type_t +0 #? #? Free-Quad Type

Reserved RAM

Address T X Y Z Description
^40000000 top next free root Memory Descriptor
^40000001 e_head e_tail k_head k_tail Events and Continuations
@60000002 #actor_t +0 () #? Device Actor #0
@60000003 #actor_t +1 () #? Device Actor #1
@60000004 #actor_t +2 () #? Device Actor #2
@60000005 #actor_t +3 () #? Device Actor #3
@60000006 #actor_t +4 () #? Device Actor #4
@60000007 #actor_t +5 () #? Device Actor #5
@60000008 #actor_t +6 () #? Device Actor #6
@60000009 #actor_t +7 () #? Device Actor #7
@6000000A #actor_t +8 () #? Device Actor #8
@6000000B #actor_t +9 () #? Device Actor #9
@6000000C #actor_t +10 () #? Device Actor #10
@6000000D #actor_t +11 () #? Device Actor #11
@6000000E #actor_t +12 () #? Device Actor #12
^4000000F memory events cycles signal Root Sponsor

Memory Descriptor

Address T X Y Z
^40000000 top addr next free free count GC root

Event and Continuation Queues

Address T X Y Z
^40000001 e_head e_tail k_head k_tail

Root Sponsor

Address T X Y Z
^4000000F memory events cycles signal

Object Graph

The diagram below shows a typical graph of quad-cells representing the contents of the e_queue (event queue) and the k_queue (continuation queue). These two queues, the interrupt-handling actors, and the root sponsor form the root-set of objects for garbage-collection.

e_queue: [e_head,e_tail]------------------------+
          |                                     V
          +-->[sponsor,to,msg,next]---> ... -->[sponsor,to,msg,()]
                       |   |
                       |   +--> actor message content
                       V
                      [#actor_t,code,data,#?]
                                 |    |
                                 |    +--> actor state
                                 |
                                 +--> actor behavior

k_queue: [k_head,k_tail]----------------+
          |                             V
          +-->[ip,sp,ep,kp]---> ... -->[ip,sp,ep,()]
               |  |  |
               |  |  +-->[sponsor,to,msg,()]
               |  |               |   |
               |  |               |   +--> ...
               |  |               V
               |  |              [#actor_t,code,data,effect]
               |  |                                   |
               |  |                                   V
               |  |                             [#actor_t,code',data',events]
               |  V                                                    |
               | [#pair_t,car,cdr,#?]                                  +--> ... -->[sponsor,to,msg,()]
               |           |   |
               |           |   +--> ... -->[#pair_t,car,(),#?]
               |           V
               |          item
               V
              [#instr_t,"eq",0,k]
                               |
                               +--> [#instr_t,"if",t,f]
                                                   | |
                                                   | +--> ...
                                                   V
                                                   ...

Pair-List Indexing

Instructions like msg, state, and nth have an immediate index argument (n) to succinctly designate parts of a pair-list.

  • Positive n designates items of the list, starting at +1
  • Negative n designates list tails, starting at -1
  • Zero designates the whole list/value
  0            -1            -2            -3
---->[car,cdr]---->[car,cdr]---->[car,cdr]---->...
    +1 |          +2 |          +3 |
       V             V             V

...or more compactly...

0-->[1,-1]-->[2,-2]-->[3,-3]--> ...
     |        |        |
     V        V        V

If the index is out-of-bounds, the result is #? (undefined).

Instructions

The uFork instruction execution engine implements a linked-stack machine, however the stack is only used for local state in a computation. The input for each instruction is taken from the stack and the output is placed back onto the stack. Instructions all have a T field containing the #instr_t type marker. The operation code is carried in the X field of the instruction. Most instructions also have an immediate value, carried in the Y field of the instruction. For the typical case of a instruction with a single continuation, the "next instruction" is carried in the Z field of the instruction.

Instructions are shown in their textual representation as defined in the assembly-language manual.

Instruction Summary

The following table summarizes the syntax and semantics of instruction statements. The Input depicts the stack before the operation. The Output depicts the stack after the operation. The top of the stack is the right-most item.

Input Instruction Output Description
push value value push literal value on stack
vₙv₁ dup n vₙv₁ vₙv₁ duplicate top n items on stack
vₙv₁ drop n remove n items from stack
vₙv₁ pick n vₙv₁ vₙ copy item n to top of stack
vₙv₁ pick -n v₁ vₙv₁ copy top of stack before item n
vₙv₁ roll n vₙ₋₁v₁ vₙ roll item n to top of stack
vₙv₁ roll -n v₁ vₙv₂ roll top of stack to item n
n alu not ~n bitwise not n
n m alu and n&m bitwise n and m
n m alu or n|m bitwise n or m
n m alu xor n^m bitwise n exclusive-or m
n m alu add n+m sum of n and m
n m alu sub n-m difference of n and m
n m alu mul n*m product of n and m
n m alu lsl n<<m logical shift left n by m
n m alu lsr n>>m logical shift right n by m
n m alu asr n>>>m arithmetic shift right n by m
n m alu rol n<<>m rotate left n by m
n m alu ror n<>>m rotate right n by m
v typeq T bool #t if v has type T, otherwise #f
u eq v bool #t if u == v, otherwise #f
u v cmp eq bool #t if u == v, otherwise #f
u v cmp ne bool #t if u != v, otherwise #f
n m cmp lt bool #t if n < m, otherwise #f
n m cmp le bool #t if n <= m, otherwise #f
n m cmp ge bool #t if n >= m, otherwise #f
n m cmp gt bool #t if n > m, otherwise #f
bool if T [F] if bool is not falsy*, continue T (else F)
k jump continue at k
tail head pair n pair create pair(s) from head and tail (n times)
vₙv₁ pair -1 (v₁vₙ) capture stack items as a single pair list
pair part n tail head split pair into head and tail (n times)
(v₁vₙ) part -1 vₙv₁ spread pair list items onto stack
(v₁vₙ . tailₙ) nth n vₙ copy item n from a pair list
(v₁vₙ . tailₙ) nth -n tailₙ copy tail n from a pair list
dict key dict has bool #t if dict has a binding for key, otherwise #f
dict key dict get value the first value bound to key in dict, or #?
dict key value dict add dict' add a binding from key to value in dict
dict key value dict set dict' replace or add a binding from key to value in dict
dict key dict del dict' remove first binding for key in dict
deque new deque an empty deque
deque deque empty bool #t if deque is empty, otherwise #f
deque value deque push deque' insert value as the first item of deque
deque deque pop deque' value remove the first value from deque, or #?
deque value deque put deque' insert value as the last item of deque
deque deque pull deque' value remove the last value from deque, or #?
deque deque len n count items in the deque
T quad 1 quad create quad [T, #?, #?, #?]
X T quad 2 quad create quad [T, X, #?, #?]
Y X T quad 3 quad create quad [T, X, Y, #?]
Z Y X T quad 4 quad create quad [T, X, Y, Z]
quad quad -1 T extract 1 quad field
quad quad -2 X T extract 2 quad fields
quad quad -3 Y X T extract 3 quad fields
quad quad -4 Z Y X T extract 4 quad fields
msg 0 msg copy event message to stack
msg n msgₙ copy message item n to stack
msg -n tailₙ copy message tail n to stack
state 0 state copy actor state to stack
state n stateₙ copy state item n to stack
state -n tailₙ copy state tail n to stack
my self actor push actor address on stack
my beh beh push actor behavior on stack
my state vₙv₁ spread actor state onto stack
mₙm₁ actor send n send (m₁mₙ) to actor
msg actor send -1 send msg to actor
sponsor mₙm₁ actor signal n send (m₁mₙ) to actor using sponsor
sponsor msg actor signal -1 send msg to actor using sponsor
vₙv₁ beh new n actor create an actor with code beh and data (v₁vₙ)
state beh new -1 actor create an actor with code beh and data state
(beh . state) new -2 actor create an actor with code beh and data state
[_, _, _, beh] new -3 actor create an actor with code beh and data [_, _, _, beh]
vₙv₁ beh beh n replace code with beh and data with (v₁vₙ)
state beh beh -1 replace code with beh and data with state
(beh . state) beh -2 replace code with beh and data with state
[_, _, _, beh] beh -3 replace code with beh and data with [_, _, _, beh]
reason end abort abort actor transaction with reason
end stop stop current continuation (thread)
end commit commit actor transaction
sponsor new sponsor create a new empty sponsor
sponsor n sponsor memory sponsor transfer n memory quota to sponsor
sponsor n sponsor events sponsor transfer n events quota to sponsor
sponsor n sponsor cycles sponsor transfer n cycles quota to sponsor
sponsor sponsor reclaim sponsor reclaim all quotas from sponsor
sponsor control sponsor start run sponsor under control
sponsor sponsor stop reclaim all quotas and remove sponsor
actual assert expect assert actual == expect, otherwise fail!
debug debugger breakpoint

* For the if instruction, the values #f, #?, #nil, and 0 are considered "falsy".

Instruction Decoding

The uFork instruction encoding is designed to allow partial decoding based on grouping similar encodings. By extracting just a few bits from the X and Y fields of the quad, we can make generalizations across instructions.

When the T field is #instr_t, the X field carries the op-code. The op-codes are all encoded as positive fixnums, so the 4 most-significant bits are 2#1000. The op-code has one of the following formats:

  • 2#1000_0000_0000_0xxx -- immediate instruction xxx
  • 2#1000_0000_0000_1xxx -- qualified instruction xxx
  • 2#1000_0000_0001_xxxx -- indexed instruction xxxx

Immediate Instructions

If bit 4 of the op-code is 0 and bit 3 is 0, this is an immediate instruction. The Y field is the immediate value. The immediate instructions are:

  • debug
  • jump
  • push
  • if
  • typeq
  • assert

The debug and jump instructions do not use the immediate value. They can be distinguish, if needed, by the 2#1000_0000_0000_000x format.

Qualifed Instructions

If bit 4 of the op-code is 0 and bit 3 is 1, this is an qualified instruction. The bottom 4 bits of the Y field define the (unsigned) qualifer value in the range [0, 15]. The qualified instructions are:

  • sponsor
  • quad
  • dict
  • deque
  • my
  • alu
  • cmp
  • end

Indexed Instructions

If bit 4 of the op-code is 1, this is an indexed instruction. The bottom 6 bits of the Y field define the (signed) index (or count) value in the range [-32, +31]. The indexed instructions are:

  • pair
  • part
  • nth
  • pick
  • roll
  • dup
  • drop
  • msg
  • state
  • send
  • signal
  • new
  • beh

Instruction Details

The semantics of each instruction are detailed below. A few general rules apply to all instructions. Unless stated otherwise in the description of an instruction:

  • Attempts to execute a non-instruction signals an error (E_NOT_EXE)
  • Unknown instruction op-codes signal an error
  • Arguments of an invalid type signal an error
  • Items referenced beyond the bottom of the stack are treated as #?

The following functions are used in various instruction descriptions:

  • Define cons(x, y) as: #pair_t(x, y)
  • Define car(x) as: if x is a #pair_t then x.X else #?
  • Define cdr(x) as: if x is a #pair_t then x.Y else #?

To Advance p by fixnum:n:

  1. While n > 0
    1. Let p become cdr(p)
    2. Let n become n-1

To Insert item at prev:

  1. If prev is a #pair_t
    1. Let entry be cons(item, cdr(prev))
    2. Set prev.Y to entry

To Extract next from prev:

  1. If prev is a #pair_t
    1. Set prev.Y to cdr(next)

To Enlist fixnum:n as list:

  1. If n > 0
    1. Let list be the stack pointer
    2. Let p be list
    3. Advance p by n
    4. If p is a #pair_t
      1. Let the stack pointer become cdr(p)
      2. Set p.Y to #nil
    5. Otherwise
      1. Let the stack pointer become #nil
  2. Otherwise
    1. Let list be #nil

To Reverse list onto head:

  1. While list is a #pair_t
    1. Let next be cdr(list)
    2. Set list.Y to head
    3. Let head become list
    4. Let list become next

To Copy list onto head:

  1. While list is a #pair_t
    1. Let head be cons(car(list), head)
    2. Let list become cdr(list)

To Copy fixnum:n of list onto head:

  1. While n > 0
    1. Let head be cons(car(list), head)
    2. Let list become cdr(list)
    3. Let n become n-1

alu instruction

Input Instruction Output Description
n alu not ~n bitwise not n
n m alu and n&m bitwise n and m
n m alu or n|m bitwise n or m
n m alu xor n^m bitwise n exclusive-or m
n m alu add n+m sum of n and m
n m alu sub n-m difference of n and m
n m alu mul n*m product of n and m
n m alu lsl n<<m logical shift left n by m
n m alu lsr n>>m logical shift right n by m
n m alu asr n>>>m arithmetic shift right n by m
n m alu rol n<<>m rotate left n by m
n m alu ror n<>>m rotate right n by m

Compute an ALU function of the arguments on the stack.

T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +0 (not) instr
  1. Remove n from the stack
  2. If n is a fixnum
    1. Invert all bits of fixnum
    2. Push result onto the stack
  3. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +1 (and) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Bitwise AND n with m
    2. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +2 (or) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Bitwise OR n with m
    2. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +3 (xor) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Bitwise XOR n with m
    2. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +4 (add) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Add n and m
    2. Truncate 2's-complement result
    3. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +5 (sub) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Subtract m from n
    2. Truncate 2's-complement result
    3. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +6 (mul) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Multiply n by m
    2. Truncate 2's-complement result
    3. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +8 (lsl) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Logical shift n left by m bits (fill w/ zero)
    2. Truncate 2's-complement result
    3. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +9 (lsr) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Logical shift n right by m bits (fill w/ zero)
    2. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +10 (asr) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Arithmetic shift n right by m bits (sign extend)
    2. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +11 (rol) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Rotate n left by m bits
    2. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +13 (alu) +12 (ror) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. Rotate n right by m bits
    2. Push result onto the stack
  4. Otherwise
    1. Push #? onto the stack

assert instruction

Input Instruction Output Description
actual assert expect assert actual == expect, otherwise fail!

Ensure that the item on the stack has the expected value.

T X (op) Y (imm) Z (k)
#instr_t +7 (assert) any instr
  1. Remove actual from the stack
  2. If actual is not equal to expect
    1. Signal an E_ASSERT error

beh instruction

Input Instruction Output Description
vₙv₁ beh beh n replace code with beh and data with (v₁vₙ)
state beh beh -1 replace code with beh and data with state
(beh . state) beh -2 replace code with beh and data with state
[_, _, _, beh] beh -3 replace code with beh and data with [_, _, _, beh]

Provide a new behavior (code and data) for the current actor. This is the actor "become" primitive. There are several ways to provide the code and data for handling the next event, however both are always replaced together.

T X (op) Y (imm) Z (k)
#instr_t +29 (beh) positive instr
  1. Remove beh from the stack
  2. Record beh as the code to execute when handling the next event
  3. Enlist positive as list
  4. Record list as the private data when handling the next event
T X (op) Y (imm) Z (k)
#instr_t +29 (beh) +0 instr
  1. Remove beh from the stack
  2. Record beh as the code to execute when handling the next event
  3. Record () as the private data when handling the next event
T X (op) Y (imm) Z (k)
#instr_t +29 (beh) -1 instr
  1. Remove beh from the stack
  2. Record beh as the code to execute when handling the next event
  3. Remove state from the stack
  4. Record state as the private data when handling the next event
T X (op) Y (imm) Z (k)
#instr_t +29 (beh) -2 instr
  1. Remove an item from the stack
  2. Record the X field of this item as the code to execute when handling the next event
  3. Record the Y field of this item as the private data when handling the next event
T X (op) Y (imm) Z (k)
#instr_t +29 (beh) -3 instr
  1. Remove an item from the stack
  2. Record the Z field of this item as the code to execute when handling the next event
  3. Record this item as the private data when handling the next event

cmp instruction

Input Instruction Output Description
u v cmp eq bool #t if u == v, otherwise #f
u v cmp ne bool #t if u != v, otherwise #f
n m cmp lt bool #t if n < m, otherwise #f
n m cmp le bool #t if n <= m, otherwise #f
n m cmp ge bool #t if n >= m, otherwise #f
n m cmp gt bool #t if n > m, otherwise #f

Compare two items from the stack. For eq and ne, raw values are compared for identity. For lt, le, ge, and gt, fixnum values are compared.

T X (op) Y (imm) Z (k)
#instr_t +14 (cmp) +0 (eq) instr
  1. Remove u from the stack
  2. Remove v from the stack
  3. If u and v are the same raw value
    1. Push #t onto the stack
  4. Otherwise
    1. Push #f onto the stack
T X (op) Y (imm) Z (k)
#instr_t +14 (cmp) +5 (ne) instr
  1. Remove u from the stack
  2. Remove v from the stack
  3. If u and v are different raw values
    1. Push #t onto the stack
  4. Otherwise
    1. Push #f onto the stack
T X (op) Y (imm) Z (k)
#instr_t +14 (cmp) +3 (lt) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. If n < m
      1. Push #t onto the stack
    2. Otherwise
      1. Push #f onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +14 (cmp) +4 (le) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. If n <= m
      1. Push #t onto the stack
    2. Otherwise
      1. Push #f onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +14 (cmp) +1 (ge) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. If n >= m
      1. Push #t onto the stack
    2. Otherwise
      1. Push #f onto the stack
  4. Otherwise
    1. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +14 (cmp) +2 (gt) instr
  1. Remove m from the stack
  2. Remove n from the stack
  3. If n and m are both fixnums
    1. If n > m
      1. Push #t onto the stack
    2. Otherwise
      1. Push #f onto the stack
  4. Otherwise
    1. Push #? onto the stack

debug instruction

Input Instruction Output Description
debug debugger breakpoint

Suspend the uFork machine and transfer control to the debugger.

T X (op) Y (imm) Z (k)
#instr_t +0 (debug) instr
  1. If the uFork machine is executing under a debugging environment
    1. Suspend the machine
    2. Transfer control to the debugger
  2. Otherwise, this instruction has no effect.

deque instruction

Input Instruction Output Description
deque new deque an empty deque
deque deque empty bool #t if deque is empty, otherwise #f
deque value deque push deque' insert value as the first item of deque
deque deque pop deque' value remove the first value from deque, or #?
deque value deque put deque' insert value as the last item of deque
deque deque pull deque' value remove the last value from deque, or #?
deque deque len n count items in the deque

Perform operations on a deque (double-ended queue). A deque is represented by a #pair_t(front, back) of stacks. The front stack contains elements ready to be taken from the queue, from first to last. The back stack contains elements put on the queue, from last to first. A deque is managed as a Banker's Queue.

T X (op) Y (imm) Z (k)
#instr_t +11 (deque) +0 (new) instr
  1. Push the empty-deque singleton #pair_t(#nil, #nil) onto the stack
T X (op) Y (imm) Z (k)
#instr_t +11 (deque) +1 (empty) instr
  1. Remove deque from the stack
  2. If deque is #pair_t(front, back)
    1. If front is a #pair_t or back is a #pair_t
      1. Push #f onto the stack
    2. Otherwise
      1. Push #t onto the stack
  3. Otherwise
    1. Push #t onto the stack
T X (op) Y (imm) Z (k)
#instr_t +11 (deque) +2 (push) instr
  1. Remove value from the stack
  2. Remove deque from the stack
  3. Push cons(cons(value, car(deque)), cdr(deque)) onto the stack
T X (op) Y (imm) Z (k)
#instr_t +11 (deque) +3 (pop) instr
  1. Remove deque from the stack
  2. If deque is #pair_t(front, back)
    1. If front is not a #pair_t
      1. While back is a #pair_t
        1. Let front become cons(car(back), front)
        2. Let back become cdr(back)
    2. Push cons(cdr(front), back) onto the stack
    3. Push car(front) onto the stack
  3. Otherwise
    1. Push deque onto the stack
    2. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +11 (deque) +4 (put) instr
  1. Remove value from the stack
  2. Remove deque from the stack
  3. Push cons(car(deque), cons(value, cdr(deque))) onto the stack
T X (op) Y (imm) Z (k)
#instr_t +11 (deque) +5 (pull) instr
  1. Remove deque from the stack
  2. If deque is #pair_t(front, back)
    1. If back is not a #pair_t
      1. While front is a #pair_t
        1. Let back become cons(car(front), back)
        2. Let front become cdr(front)
    2. Push cons(front, cdr(back)) onto the stack
    3. Push car(back) onto the stack
  3. Otherwise
    1. Push deque onto the stack
    2. Push #? onto the stack
T X (op) Y (imm) Z (k)
#instr_t +11 (deque) +6 (len) instr
  1. Remove deque from the stack
  2. Let count be 0
  3. Let front be car(deque)
  4. While front is a #pair_t
    1. Let count become count+1
    2. Let front become cdr(front)
  5. Let back be cdr(deque)
  6. While back is a #pair_t
    1. Let count become count+1
    2. Let back become cdr(back)
  7. Push count onto the stack

dict instruction

Input Instruction Output Description
dict key dict has bool #t if dict has a binding for key, otherwise #f
dict key dict get value the first value bound to key in dict, or #?
dict key value dict add dict' add a binding from key to value in dict
dict key value dict set dict' replace or add a binding from key to value in dict
dict key dict del dict' remove first binding for key in dict

Perform operations on a dictionary (key-value store). An empty dictionary is represented by #nil (or any non-#dict_t). A dictionary entry is represented by a #dict_t(key, value, next).

T X (op) Y (imm) Z (k)
#instr_t +10 (dict) +0 (has) instr
  1. Remove key from the stack
  2. Remove dict from the stack
  3. Let found be #f
  4. While not found and dict is #dict_t(k, v, next)
    1. If k == key
      1. Let found become #t
    2. Otherwise
      1. Let dict become next
  5. Push found onto the stack
T X (op) Y (imm) Z (k)
#instr_t +10 (dict) +1 (get) instr
  1. Remove key from the stack
  2. Remove dict from the stack
  3. Let found be #?
  4. While found == #? and dict is #dict_t(k, v, next)
    1. If k == key
      1. Let found become v
    2. Otherwise
      1. Let dict become next
  5. Push found onto the stack
T X (op) Y (imm) Z (k)
#instr_t +10 (dict) +2 (add) instr
  1. Remove value from the stack
  2. Remove key from the stack
  3. Remove dict from the stack
  4. Push #dict_t(key, value, dict) onto the stack
T X (op) Y (imm) Z (k)
#instr_t +10 (dict) +3 (set) instr
  1. Remove value from the stack
  2. Remove key from the stack
  3. Remove dict from the stack
  4. If dict_has(key)
    1. Let dict become dict_del(key)
  5. Push #dict_t(key, value, dict) onto the stack
T X (op) Y (imm) Z (k)
#instr_t +10 (dict) +4 (del) instr
  1. Remove key from the stack
  2. Remove dict from the stack
  3. Let orig be dict
  4. Let copy be #nil
  5. While dict is #dict_t(k, v, next)
    1. Let dict become next
    2. If k == key
      1. While copy is #dict_t(k, v, next)
        1. Let dict become #dict_t(k, v, dict)
        2. Let copy become next
      2. Push dict onto the stack
      3. Return
    3. Let copy become #dict_t(k, v, copy)
  6. Push orig onto the stack

drop instruction

Input Instruction Output Description
vₙv₁ drop n remove n items from stack

Discard items from the stack.

T X (op) Y (imm) Z (k)
#instr_t +23 (drop) n instr
  1. While n > 0
    1. Remove from the stack
    2. Let n become n-1

dup instruction

Input Instruction Output Description
vₙv₁ dup n vₙv₁ vₙv₁ duplicate top n items on stack

Duplicate items on the top of the stack.

T X (op) Y (imm) Z (k)
#instr_t +22 (dup) n instr
  1. Let scan be the stack pointer
  2. Let copy be #nil
  3. Copy n of scan onto copy
  4. Reverse copy onto the stack pointer

end instruction

Input Instruction Output Description
reason end abort abort actor transaction with reason
end stop stop current continuation (thread)
end commit commit actor transaction

End the current actor message-event processing transaction. This instruction has no k field, so the continuation is not re-queued and the processing "thread" ends.

T X (op) Y (imm) Z (k)
#instr_t +15 (end) -1 (abort)
  1. Remove reason from the stack
  2. Record/report reason to system-specific log or debugger
  3. Discard the current event and any pending effects
  4. Make actor ready for the next event
T X (op) Y (imm) Z (k)
#instr_t +15 (end) +0 (stop)
  1. Signal an E_STOP error
T X (op) Y (imm) Z (k)
#instr_t +15 (end) +1 (commit)
  1. Update the current actor's state and behavior
  2. Add sent message-events to the message-queue
  3. Discard the current event
  4. Make actor ready for the next event

eq instruction

Input Instruction Output Description
u eq v bool #t if u == v, otherwise #f

Compare the top-of-stack item to an immediate value.

T X (op) Y (imm) Z (k)
#instr_t +6 (eq) v instr
  1. Remove u from the stack
  2. If u and v are the same raw value
    1. Push #t onto the stack
  3. Otherwise
    1. Push #f onto the stack

if instruction

Input Instruction Output Description
bool if T [F] if bool is not falsy*, continue T (else F)
bool if_not F [T] if bool is falsy*, continue F (else T)

* The values #f, #?, #nil, and 0 are considered "falsy".

Select one of two continuations based on the value on the top of the stack. Note: Both if and if_not generate the same machine instruction.

T X (op) Y (imm) Z (k)
#instr_t +3 (if) t-instr f-instr
  1. Remove bool from the stack
  2. If bool is "falsy"
    1. Continue execution at f-instr
  3. Otherwise
    1. Continue execution at t-instr

jump instruction

Input Instruction Output Description
k jump continue at k

Continue execution at the address taken from the stack.

T (type) X (op) Y (imm) Z (k)
#instr_t +1 (jump) #? #?
  1. Remove k from the stack
  2. If k is an #instr_t
    1. Continue execution at k
  3. Otherwise
    1. Signal an error (E_NOT_EXE)

msg instruction

Input Instruction Output Description
msg 0 msg copy event message to stack
msg n msgₙ copy message item n to stack
msg -n tailₙ copy message tail n to stack

Copy data from the current message-event.

T (type) X (op) Y (imm) Z (k)
#instr_t +24 (msg) n instr
  1. Let msg be the entire message
  2. Push nth(n, msg) onto the stack

my instruction

Input Instruction Output Description
my self actor push actor address on stack
my beh beh push actor behavior on stack
my state vₙv₁ spread actor state onto stack

Copy data relating to the current actor (see new, beh, and state).

T X (op) Y (imm) Z (k)
#instr_t +12 (my) +0 (self) instr
  1. Push the current actor capability onto the stack
T X (op) Y (imm) Z (k)
#instr_t +12 (my) +1 (beh) instr
  1. Push the current actor's code address onto the stack
T X (op) Y (imm) Z (k)
#instr_t +12 (my) +2 (state) instr
  1. Let list be the current actor's data
  2. Push part(-1, list) onto the stack

new instruction

Input Instruction Output Description
vₙv₁ beh new n actor create an actor with code beh and data (v₁vₙ)
state beh new -1 actor create an actor with code beh and data state
(beh . state) new -2 actor create an actor with code beh and data state
[_, _, _, beh] new -3 actor create an actor with code beh and data [_, _, _, beh]

Provide the behavior (code and data) for a new actor. This is the actor "create" primitive. There are several ways to provide the code and data for the new actor, however both are always specified together.

T X (op) Y (imm) Z (k)
#instr_t +28 (new) positive instr
  1. Remove beh from the stack
  2. Enlist positive as list
  3. Create a new actor with beh for code and list for data
  4. Push a capability designating the new actor onto the stack
T X (op) Y (imm) Z (k)
#instr_t +28 (new) +0 instr
  1. Remove beh from the stack
  2. Create a new actor with beh for code and () for data
  3. Push a capability designating the new actor onto the stack
T X (op) Y (imm) Z (k)
#instr_t +28 (new) -1 instr
  1. Remove beh from the stack
  2. Remove state from the stack
  3. Create a new actor with beh for code and state for data
  4. Push a capability designating the new actor onto the stack
T X (op) Y (imm) Z (k)
#instr_t +28 (new) -2 instr
  1. Remove an item from the stack
  2. Create a new actor with the X field of this item for code and the Y field of this item for data
  3. Push a capability designating the new actor onto the stack
T X (op) Y (imm) Z (k)
#instr_t +28 (new) -3 instr
  1. Remove an item from the stack
  2. Create a new actor with the Z field of this item for code and this item for data
  3. Push a capability designating the new actor onto the stack

nth instruction

Input Instruction Output Description
(v₁vₙ . tailₙ) nth n vₙ copy item n from a pair list
(v₁vₙ . tailₙ) nth -n tailₙ copy tail n from a pair list

Extract data from a pair-list.

T (type) X (op) Y (imm) Z (k)
#instr_t +19 (nth) +0 instr
  1. No effect
T (type) X (op) Y (imm) Z (k)
#instr_t +19 (nth) positive instr
  1. Remove list from the stack
  2. Let n be positive-1
  3. Let scan be list
  4. While n > 0
    1. Let scan become cdr(scan)
    2. Let n become n-1
  5. Push car(scan) onto the stack
T (type) X (op) Y (imm) Z (k)
#instr_t +19 (nth) negative instr
  1. Remove list from the stack
  2. Let n be negative+1
  3. Let scan be list
  4. While n < 0
    1. Let scan become cdr(scan)
    2. Let n become n+1
  5. Push cdr(scan) onto the stack

pair instruction

Input Instruction Output Description
tail head pair n pair create pair(s) from head and tail (n times)
vₙv₁ pair -1 (v₁vₙ) capture stack items as a single pair list

Create a pair-list from some number of stack items.

T (type) X (op) Y (imm) Z (k)
#instr_t +17 (pair) +0 instr
  1. Push () onto the stack
T (type) X (op) Y (imm) Z (k)
#instr_t +17 (pair) positive instr
  1. Let list be the stack pointer
  2. Let scan be list
  3. Advance scan by positive-1
  4. If scan is a #pair_t
    1. Let tail be cdr(scan)
    2. Set scan.Y to car(tail)
    3. If tail is a #pair_t
      1. Set tail.X to list
      2. Let the stack pointer become tail
    4. Otherwise
      1. Let the stack pointer become cons(list, #nil)
  5. Otherwise
    1. Let the stack pointer become cons(list, #nil)
T (type) X (op) Y (imm) Z (k)
#instr_t +17 (pair) negative instr
  1. If negative is -1
    1. Let list be the stack pointer
    2. Let the stack pointer become cons(list, #nil)
  2. Otherwise
    1. Push #? onto the stack

part instruction

Input Instruction Output Description
pair part n tail head split pair into head and tail (n times)
(v₁vₙ) part -1 vₙv₁ spread pair list items onto stack

Split items from a pair-list onto the stack.

T X (op) Y (imm) Z (k)
#instr_t +18 (part) n instr
  1. Remove pair from the stack
  2. Let copy be #nil
  3. Copy n of pair onto copy
  4. Push pair onto the stack
  5. Reverse copy onto the stack
T X (op) Y (imm) Z (k)
#instr_t +18 (part) negative instr
  1. If negative is -1
    1. Remove pair from the stack
    2. Let copy be #nil
    3. Copy pair onto copy
    4. Reverse copy onto the stack
  2. Otherwise
    1. Push #? onto the stack

pick instruction

Input Instruction Output Description
vₙv₁ pick n vₙv₁ vₙ copy item n to top of stack
vₙv₁ pick -n v₁ vₙv₁ copy top of stack before item n

Copy an item at a particular depth on the stack.

T (type) X (op) Y (imm) Z (k)
#instr_t +20 (pick) +0 instr
  1. Push #? onto the stack
T (type) X (op) Y (imm) Z (k)
#instr_t +20 (pick) positive instr
  1. Let n be positive-1
  2. Let scan be the stack pointer
  3. Advance scan by n
  4. Push car(scan) onto the stack
T (type) X (op) Y (imm) Z (k)
#instr_t +20 (pick) negative instr
  1. Let n be -negative-1
  2. Let scan be the stack pointer
  3. Let item be car(scan)
  4. Advance scan by n
  5. Insert item at scan

push instruction

Input Instruction Output Description
push value value push literal value on stack

Push an immediate value onto the top of the stack.

T (type) X (op) Y (imm) Z (k)
#instr_t +2 (push) any instr
  1. Push value onto the stack

roll instruction

Input Instruction Output Description
vₙv₁ roll n vₙ₋₁v₁ vₙ roll item n to top of stack
vₙv₁ roll -n v₁ vₙv₂ roll top of stack to item n

Rotate stack items up to a particular depth.

T (type) X (op) Y (imm) Z (k)
#instr_t +21 (roll) +0 instr
  1. No effect
T (type) X (op) Y (imm) Z (k)
#instr_t +21 (roll) positive instr
  1. Let n be positive-1
  2. If n > 0
    1. Let p be the stack pointer
    2. Advance p by n-1
    3. Let q be cdr(p)
    4. Extract q from p
    5. Push car(q) onto the stack
T (type) X (op) Y (imm) Z (k)
#instr_t +21 (roll) negative instr
  1. Let n be -negative-1
  2. If n > 0
    1. Remove item from the stack
    2. Let p be the stack pointer
    3. Advance p by n-1
    4. Insert item at p

send instruction

Input Instruction Output Description
mₙm₁ actor send n send (m₁mₙ) to actor
msg actor send -1 send msg to actor

Add a new message-event to the current actor's transactional effects.

T X (op) Y (imm) Z (k)
#instr_t +26 (send) positive instr
  1. Remove actor from the stack
  2. Enlist positive as msg
  3. Record in the current actor's effect a new event with:
    • actor as the target
    • msg as the message
T X (op) Y (imm) Z (k)
#instr_t +26 (send) +0 instr
  1. Remove actor from the stack
  2. Record in the current actor's effect a new event with:
    • actor as the target
    • () as the message
T X (op) Y (imm) Z (k)
#instr_t +26 (send) -1 instr
  1. Remove actor from the stack
  2. Remove msg from the stack
  3. Record in the current actor's effect a new event with:
    • actor as the target
    • msg as the message

signal instruction

Input Instruction Output Description
sponsor mₙm₁ actor signal n send (m₁mₙ) to actor using sponsor
sponsor msg actor signal -1 send msg to actor using sponsor

Add a new "sponsored" message-event to the current actor's transactional effects.

T X (op) Y (imm) Z (k)
#instr_t +27 (signal) positive instr
  1. Remove actor from the stack
  2. Enlist positive as msg
  3. Remove sponsor from the stack
  4. Record in the current actor's effect a new event with:
    • sponsor as the sponsor
    • actor as the target
    • msg as the message
T X (op) Y (imm) Z (k)
#instr_t +27 (signal) +0 instr
  1. Remove actor from the stack
  2. Remove sponsor from the stack
  3. Record in the current actor's effect a new event with:
    • sponsor as the sponsor
    • actor as the target
    • () as the message
T X (op) Y (imm) Z (k)
#instr_t +27 (signal) -1 instr
  1. Remove actor from the stack
  2. Remove msg from the stack
  3. Remove sponsor from the stack
  4. Record in the current actor's effect a new event with:
    • sponsor as the sponsor
    • actor as the target
    • msg as the message

sponsor instruction

Input Instruction Output Description
sponsor new sponsor create a new empty sponsor
sponsor n sponsor memory sponsor transfer n memory quota to sponsor
sponsor n sponsor events sponsor transfer n events quota to sponsor
sponsor n sponsor cycles sponsor transfer n cycles quota to sponsor
sponsor sponsor reclaim sponsor reclaim all quotas from sponsor
sponsor control sponsor start run sponsor under control
sponsor sponsor stop reclaim all quotas and remove sponsor

Manage Sponsorships. Sponsors are associated with events, not actors. The "current sponsor" is the sponsor of the current event. An actor cannot access the current sponsor directly. Also, sponsors cannot be created by quad because they don't have a #type_t in the T field.

T X (op) Y (imm) Z (k)
#instr_t +8 (sponsor) +0 (new) instr
  1. Let sponsor be a new sponsor with quotas:
    • memory = 0
    • events = 0
    • cycles = 0
  2. Push sponsor onto the stack
T X (op) Y (imm) Z (k)
#instr_t +8 (sponsor) +1 (memory) instr
  1. Remove n from the stack
  2. If n < 0
    1. Signal an error (E_BOUNDS)
  3. Otherwise
    1. If the current sponsor has less than n memory available
      1. Signal an error (E_MEM_LIM)
    2. Otherwise
      1. Subtract n to current sponsor memory
      2. Add n to sponsor memory
T X (op) Y (imm) Z (k)
#instr_t +8 (sponsor) +2 (events) instr
  1. Remove n from the stack
  2. If n < 0
    1. Signal an error (E_BOUNDS)
  3. Otherwise
    1. If the current sponsor has less than n events available
      1. Signal an error (E_MSG_LIM)
    2. Otherwise
      1. Subtract n to current sponsor events
      2. Add n to sponsor events
T X (op) Y (imm) Z (k)
#instr_t +8 (sponsor) +3 (cycles) instr
  1. Remove n from the stack
  2. If n < 0
    1. Signal an error (E_BOUNDS)
  3. Otherwise
    1. If the current sponsor has less than n cycles available
      1. Signal an error (E_CPU_LIM)
    2. Otherwise
      1. Subtract n to current sponsor cycles
      2. Add n to sponsor cycles
T X (op) Y (imm) Z (k)
#instr_t +8 (sponsor) +4 (reclaim) instr
  1. Add memory quota from sponsor to current sponsor (saturating)
  2. Add events quota from sponsor to current sponsor (saturating)
  3. Add cycles quota from sponsor to current sponsor (saturating)
  4. Set memory, events, and cycles quota in sponsor to 0
T X (op) Y (imm) Z (k)
#instr_t +8 (sponsor) +5 (start) instr
  1. Remove control from the stack
  2. If control is a #actor_t
    1. Set signal in sponsor to control (runable)
    2. Remove sponsor from the stack
  3. Otherwise
    1. Signal an error (E_NOT_CAP)
T X (op) Y (imm) Z (k)
#instr_t +8 (sponsor) +6 (stop) instr
  1. sponsor_reclaim(sponsor)
  2. Set signal in sponsor to 0 (stopped)
  3. Remove sponsor from the stack

state instruction

Input Instruction Output Description
state 0 state copy actor state to stack
state n stateₙ copy state item n to stack
state -n tailₙ copy state tail n to stack

Copy data from the current actor's state.

T (type) X (op) Y (imm) Z (k)
#instr_t +25 (state) n instr
  1. Let state be the current actor's state
  2. Push nth(n, state) onto the stack

typeq instruction

Input Instruction Output Description
v typeq T bool #t if v has type T, otherwise #f

Check the type of an arbitrary value. Core types are represented by constants in low ROM. Custom types may be defined in ROM or RAM. Values with #fixnum_t or #actor_t types can be classified from their type-tags. Other values carry their type in the T field of their quad (see Representation).

T X (op) Y (imm) Z (k)
#instr_t +5 (typeq) T instr
  1. Remove v from the stack
  2. If v has type T
    1. Push #t onto the stack
  3. Otherwise
    1. Push #f onto the stack

quad instruction

Input Instruction Output Description
T quad 1 quad create quad [T, #?, #?, #?]
X T quad 2 quad create quad [T, X, #?, #?]
Y X T quad 3 quad create quad [T, X, Y, #?]
Z Y X T quad 4 quad create quad [T, X, Y, Z]
quad quad -1 T extract 1 quad field
quad quad -2 X T extract 2 quad fields
quad quad -3 Y X T extract 3 quad fields
quad quad -4 Z Y X T extract 4 quad fields

Allocate and initialize, or access, a cell in quad-memory (RAM). The T field must designate an explicit type. User-defined types can be created with T = #type_t and X = the arity (number of data fields) from 0 to 3.

T X (op) Y (imm) Z (k)
#instr_t +9 (quad) +1 instr
  1. Remove T from the stack
  2. If T is a #type_t
    1. If type T has arity 0
      1. Allocate a new quad intialized to [T, #?, #?, #?]
      2. Push quad reference onto the stack
    2. Otherwise
      1. Signal an error (E_BOUNDS)
  3. Otherwise
    1. Signal an error (E_NO_TYPE)
T X (op) Y (imm) Z (k)
#instr_t +9 (quad) +2 instr
  1. Remove T from the stack
  2. Remove X from the stack
  3. If T is a #type_t
    1. If type T has arity 1
      1. Allocate a new quad intialized to [T, X, #?, #?]
      2. Push quad reference onto the stack
    2. Otherwise
      1. Signal an error (E_BOUNDS)
  4. Otherwise
    1. Signal an error (E_NO_TYPE)
T X (op) Y (imm) Z (k)
#instr_t +9 (quad) +3 instr
  1. Remove T from the stack
  2. Remove X from the stack
  3. Remove Y from the stack
  4. If T is a #type_t
    1. If type T has arity 2
      1. Allocate a new quad intialized to [T, X, Y, #?]
      2. Push quad reference onto the stack
    2. Otherwise
      1. Signal an error (E_BOUNDS)
  5. Otherwise
    1. Signal an error (E_NO_TYPE)
T X (op) Y (imm) Z (k)
#instr_t +9 (quad) +4 instr
  1. Remove T from the stack
  2. Remove X from the stack
  3. Remove Y from the stack
  4. Remove Z from the stack
  5. If T is a #type_t
    1. If type T has arity 3
      1. Allocate a new quad intialized to [T, X, Y, Z]
      2. Push quad reference onto the stack
    2. Otherwise
      1. Signal an error (E_BOUNDS)
  6. Otherwise
    1. Signal an error (E_NO_TYPE)
T X (op) Y (imm) Z (k)
#instr_t +9 (quad) -1 instr
  1. Remove quad from the stack
  2. Push quad field T onto the stack
T X (op) Y (imm) Z (k)
#instr_t +9 (quad) -2 instr
  1. Remove quad from the stack
  2. Push quad field X onto the stack
  3. Push quad field T onto the stack
T X (op) Y (imm) Z (k)
#instr_t +9 (quad) -3 instr
  1. Remove quad from the stack
  2. Push quad field Y onto the stack
  3. Push quad field X onto the stack
  4. Push quad field T onto the stack
T X (op) Y (imm) Z (k)
#instr_t +9 (quad) -4 instr
  1. Remove quad from the stack
  2. Push quad field Z onto the stack
  3. Push quad field Y onto the stack
  4. Push quad field X onto the stack
  5. Push quad field T onto the stack