You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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, code, data, #?]
idle actor
[#actor_t, code, data, effects]
busy actor
[#actor_t, code', data', events]
effects, initial events=#nil
[#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
#nil
^00000001
#?
#?
#?
#?
Nil (empty list)
#f
^00000002
#?
#?
#?
#?
Boolean False
#t
^00000003
#?
#?
#?
#?
Boolean True
--
^00000004
#?
#?
#?
#?
--reserved--
EMPTY_DQ
^00000005
#pair_t
#nil
#nil
#?
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
#nil
#?
Device Actor #0
@60000003
#actor_t
+1
#nil
#?
Device Actor #1
@60000004
#actor_t
+2
#nil
#?
Device Actor #2
@60000005
#actor_t
+3
#nil
#?
Device Actor #3
@60000006
#actor_t
+4
#nil
#?
Device Actor #4
@60000007
#actor_t
+5
#nil
#?
Device Actor #5
@60000008
#actor_t
+6
#nil
#?
Device Actor #6
@60000009
#actor_t
+7
#nil
#?
Device Actor #7
@6000000A
#actor_t
+8
#nil
#?
Device Actor #8
@6000000B
#actor_t
+9
#nil
#?
Device Actor #9
@6000000C
#actor_t
+10
#nil
#?
Device Actor #10
@6000000D
#actor_t
+11
#nil
#?
Device Actor #11
@6000000E
#actor_t
+12
#nil
#?
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,#nil]
| | |
| | +--> actor message content
| V
| [#actor_t,code,data,#?]
V | |
[memory,events,cycles,signal] | +--> actor state
|
+--> actor behavior
k_queue: [k_head,k_tail]----------------+
| V
+-->[ip,sp,ep,kp]---> ... -->[ip,sp,ep,#nil]
| | |
| | V
| | [sponsor,to,msg,#nil]
| | | |
| | | +--> ...
| | V
| | [#actor_t,code,data,effect]
| | |
| | V
| | [#actor_t,code',data',events]
| V |
| [#pair_t,car,cdr,#?] +--> ... -->[sponsor,to,msg,#nil]
| | |
| | +--> ... -->[#pair_t,car,#nil,#?]
| 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
—
pushvalue
value
push literal value on stack
vₙ … v₁
dupn
vₙ … v₁vₙ … v₁
duplicate top n items on stack
vₙ … v₁
dropn
—
remove n items from stack
vₙ … v₁
pickn
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₁
rolln
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
alunot
~n
bitwise not n
nm
aluand
n&m
bitwise n and m
nm
aluor
n|m
bitwise n or m
nm
aluxor
n^m
bitwise n exclusive-or m
nm
aluadd
n+m
sum of n and m
nm
alusub
n-m
difference of n and m
nm
alumul
n*m
product of n and m
nm
aludiv
qr
Euclidean quotient and remainder/modulus
nm
alulsl
n<<m
logical shift left n by m
nm
alulsr
n>>m
logical shift right n by m
nm
aluasr
n>>>m
arithmetic shift right n by m
nm
alurol
n<<>m
rotate left n by m
nm
aluror
n<>>m
rotate right n by m
v
typeqT
bool
#t if v has type T, otherwise #f
u
eqv
bool
#t if u == v, otherwise #f
uv
cmpeq
bool
#t if u == v, otherwise #f
uv
cmpne
bool
#t if u != v, otherwise #f
nm
cmplt
bool
#t if n < m, otherwise #f
nm
cmple
bool
#t if n <= m, otherwise #f
nm
cmpge
bool
#t if n >= m, otherwise #f
nm
cmpgt
bool
#t if n > m, otherwise #f
bool
ifT [F]
—
if bool is not falsy*, continue T (else F)
k
jump
—
continue at k
… tailhead
pairn
pair
create pair(s) from head and tail (n times)
pair
partn
… tailhead
split pair into head and tail (n times)
v₁,…,vₙ,tailₙ
nthn
vₙ
copy item n from a pair list
v₁,…,vₙ,tailₙ
nth -n
tailₙ
copy tail n from a pair list
dictkey
dicthas
bool
#t if dict has a binding for key, otherwise #f
dictkey
dictget
value
the first value bound to key in dict, or #?
dictkeyvalue
dictadd
dict'
add a binding from key to value in dict
dictkeyvalue
dictset
dict'
replace or add a binding from key to value in dict
dictkey
dictdel
dict'
remove first binding for key in dict
—
dequenew
deque
an empty deque
deque
dequeempty
bool
#t if deque is empty, otherwise #f
dequevalue
dequepush
deque'
insert value as the first item of deque
deque
dequepop
deque'value
remove the first value from deque, or #?
dequevalue
dequeput
deque'
insert value as the last item of deque
deque
dequepull
deque'value
remove the last value from deque, or #?
deque
dequelen
n
count items in the deque
T
quad1
quad
create quad [T, #?, #?, #?]
XT
quad2
quad
create quad [T, X, #?, #?]
YXT
quad3
quad
create quad [T, X, Y, #?]
ZYXT
quad4
quad
create quad [T, X, Y, Z]
quad
quad-1
T
extract 1 quad field
quad
quad-2
XT
extract 2 quad fields
quad
quad-3
YXT
extract 3 quad fields
quad
quad-4
ZYXT
extract 4 quad fields
—
msg0
msg
copy event message to stack
—
msgn
msgₙ
copy message item n to stack
—
msg -n
tailₙ
copy message tail n to stack
—
state0
state
copy actor state to stack
—
staten
stateₙ
copy state item n to stack
—
state -n
tailₙ
copy state tail n to stack
msgactor
actorsend
—
send msg to actor
spnmsgactor
actorpost
—
send msg to actor using sponsor spn
statebeh
actorcreate
actor
create an actor with code beh and data state
statebeh
actorbecome
—
replace code with beh and data with state
—
actorself
actor
push actor address on stack
reason
endabort
—
abort actor transaction with reason
—
endstop
—
stop current continuation (thread)
—
endcommit
—
commit actor transaction
—
sponsornew
sponsor
create a new empty sponsor
sponsorn
sponsormemory
sponsor
transfer n memory quota to sponsor
sponsorn
sponsorevents
sponsor
transfer n events quota to sponsor
sponsorn
sponsorcycles
sponsor
transfer n cycles quota to sponsor
sponsor
sponsorreclaim
sponsor
reclaim all quotas from sponsor
sponsorcontrol
sponsorstart
—
run sponsor under control
sponsor
sponsorstop
—
reclaim all quotas and remove sponsor
actual
assertexpect
—
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:
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 distinguished, 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
actor
dict
deque
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:
quad
pair
part
nth
pick
roll
dup
drop
msg
state
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:
While n > 0
Let p become cdr(p)
Let n become n-1
To Insert item at prev:
If prev is a #pair_t
Let entry be cons(item, cdr(prev))
Set prev.Y to entry
To Extract next from prev:
If prev is a #pair_t
Set prev.Y to cdr(next)
To Reverse list onto head:
While list is a #pair_t
Let next be cdr(list)
Set list.Y to head
Let head become list
Let list become next
To Copy list onto head:
While list is a #pair_t
Let head be cons(car(list), head)
Let list become cdr(list)
To Copy fixnum:n of list onto head:
While n > 0
Let head be cons(car(list), head)
Let list become cdr(list)
Let n become n-1
actor instruction
Input
Instruction
Output
Description
msgactor
actorsend
—
send msg to actor
spnmsgactor
actorpost
—
send msg to actor using sponsor spn
statebeh
actorcreate
actor
create an actor with code beh and data state
statebeh
actorbecome
—
replace code with beh and data with state
—
actorself
actor
push actor address on stack
Record effects of actor primitives.
Note that effects are not released into the system
until and unless the actor executes endcommit.
T
X (op)
Y (imm)
Z (k)
#instr_t
+9 (actor)
+0 (send)
instr
Remove actor from the stack
Remove msg from the stack
If actor is a capability
Record in the current actor's effect a new event with:
the sponsor of the current event as the sponsor
actor as the target
msg as the message
Otherwise
Abort transaction with E_NOT_CAP reason
T
X (op)
Y (imm)
Z (k)
#instr_t
+9 (actor)
+1 (post)
instr
Remove actor from the stack
Remove msg from the stack
Remove spn from the stack
If actor is a capability and spn is a sponsor
Record in the current actor's effect a new event with:
spn as the sponsor
actor as the target
msg as the message
Otherwise
Abort transaction with E_NOT_CAP reason
T
X (op)
Y (imm)
Z (k)
#instr_t
+9 (actor)
+2 (create)
instr
Remove beh from the stack
Remove state from the stack
If beh is an instruction
Create a new actor with beh for code and state for data
Push a capability designating the new actor onto the stack
Otherwise
Abort transaction with E_NOT_EXE reason
T
X (op)
Y (imm)
Z (k)
#instr_t
+9 (actor)
+3 (become)
instr
Remove beh from the stack
Remove state from the stack
If beh is an instruction
Record beh as the code to execute when handling the next event
Record state as the private data when handling the next event
Otherwise
Abort transaction with E_NOT_EXE reason
T
X (op)
Y (imm)
Z (k)
#instr_t
+9 (actor)
+4 (self)
instr
Push the current actor capability onto the stack
alu instruction
Input
Instruction
Output
Description
n
alunot
~n
bitwise not n
nm
aluand
n&m
bitwise n and m
nm
aluor
n|m
bitwise n or m
nm
aluxor
n^m
bitwise n exclusive-or m
nm
aluadd
n+m
sum of n and m
nm
alusub
n-m
difference of n and m
nm
alumul
n*m
product of n and m
nd
aludiv
rq
Euclidean division
nm
alulsl
n<<m
logical shift left n by m
nm
alulsr
n>>m
logical shift right n by m
nm
aluasr
n>>>m
arithmetic shift right n by m
nm
alurol
n<<>m
rotate left n by m
nm
aluror
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
Remove n from the stack
If n is a fixnum
Invert all bits of fixnum
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+1 (and)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Bitwise AND n with m
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+2 (or)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Bitwise OR n with m
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+3 (xor)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Bitwise XOR n with m
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+4 (add)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Add n and m
Truncate 2's-complement result
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+5 (sub)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Subtract m from n
Truncate 2's-complement result
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+6 (mul)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Multiply n by m
Truncate 2's-complement result
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+7 (div)
instr
The immediate value +7 (div) is reserved for fixnum division.
q = n / d and r = n % d where, n = dq + r
There are several reasonable definitions.
We choose Euclidean, where 0 ≤ r < |d|.
n
d
q
r
+17
+5
+3
+2
-17
+5
-4
+3
+17
-5
-3
+2
-17
-5
+4
+3
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+8 (lsl)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Logical shift n left by m bits (fill w/ zero)
Truncate 2's-complement result
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+9 (lsr)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Logical shift n right by m bits (fill w/ zero)
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+10 (asr)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Arithmetic shift n right by m bits (sign extend)
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+11 (rol)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Rotate n left by m bits
Push result onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+13 (alu)
+12 (ror)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
Rotate n right by m bits
Push result onto the stack
Otherwise
Push #? onto the stack
assert instruction
Input
Instruction
Output
Description
actual
assertexpect
—
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
Remove actual from the stack
If actual is not equal to expect
Signal an E_ASSERT error
cmp instruction
Input
Instruction
Output
Description
uv
cmpeq
bool
#t if u == v, otherwise #f
uv
cmpne
bool
#t if u != v, otherwise #f
nm
cmplt
bool
#t if n < m, otherwise #f
nm
cmple
bool
#t if n <= m, otherwise #f
nm
cmpge
bool
#t if n >= m, otherwise #f
nm
cmpgt
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
Remove u from the stack
Remove v from the stack
If u and v are the same raw value
Push #t onto the stack
Otherwise
Push #f onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+14 (cmp)
+5 (ne)
instr
Remove u from the stack
Remove v from the stack
If u and v are different raw values
Push #t onto the stack
Otherwise
Push #f onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+14 (cmp)
+3 (lt)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
If n < m
Push #t onto the stack
Otherwise
Push #f onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+14 (cmp)
+4 (le)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
If n <= m
Push #t onto the stack
Otherwise
Push #f onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+14 (cmp)
+1 (ge)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
If n >= m
Push #t onto the stack
Otherwise
Push #f onto the stack
Otherwise
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+14 (cmp)
+2 (gt)
instr
Remove m from the stack
Remove n from the stack
If n and m are both fixnums
If n > m
Push #t onto the stack
Otherwise
Push #f onto the stack
Otherwise
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
If the uFork machine is executing under a debugging environment
Suspend the machine
Transfer control to the debugger
Otherwise, this instruction has no effect.
deque instruction
Input
Instruction
Output
Description
—
dequenew
deque
an empty deque
deque
dequeempty
bool
#t if deque is empty, otherwise #f
dequevalue
dequepush
deque'
insert value as the first item of deque
deque
dequepop
deque'value
remove the first value from deque, or #?
dequevalue
dequeput
deque'
insert value as the last item of deque
deque
dequepull
deque'value
remove the last value from deque, or #?
deque
dequelen
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
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
Remove deque from the stack
If deque is #pair_t(front, back)
If front is a #pair_t or back is a #pair_t
Push #f onto the stack
Otherwise
Push #t onto the stack
Otherwise
Push #t onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+11 (deque)
+2 (push)
instr
Remove value from the stack
Remove deque from the stack
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
Remove deque from the stack
If deque is #pair_t(front, back)
If front is not a #pair_t
While back is a #pair_t
Let front become cons(car(back), front)
Let back become cdr(back)
Push cons(cdr(front), back) onto the stack
Push car(front) onto the stack
Otherwise
Push deque onto the stack
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+11 (deque)
+4 (put)
instr
Remove value from the stack
Remove deque from the stack
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
Remove deque from the stack
If deque is #pair_t(front, back)
If back is not a #pair_t
While front is a #pair_t
Let back become cons(car(front), back)
Let front become cdr(front)
Push cons(front, cdr(back)) onto the stack
Push car(back) onto the stack
Otherwise
Push deque onto the stack
Push #? onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+11 (deque)
+6 (len)
instr
Remove deque from the stack
Let count be 0
Let front be car(deque)
While front is a #pair_t
Let count become count+1
Let front become cdr(front)
Let back be cdr(deque)
While back is a #pair_t
Let count become count+1
Let back become cdr(back)
Push count onto the stack
dict instruction
Input
Instruction
Output
Description
dictkey
dicthas
bool
#t if dict has a binding for key, otherwise #f
dictkey
dictget
value
the first value bound to key in dict, or #?
dictkeyvalue
dictadd
dict'
add a binding from key to value in dict
dictkeyvalue
dictset
dict'
replace or add a binding from key to value in dict
dictkey
dictdel
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).
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)
#?
Remove reason from the stack
Record/report reason to system-specific log or debugger
Discard the current event and any pending effects
Make actor ready for the next event
T
X (op)
Y (imm)
Z (k)
#instr_t
+15 (end)
+0 (stop)
#?
Signal an E_STOP error
T
X (op)
Y (imm)
Z (k)
#instr_t
+15 (end)
+1 (commit)
#?
Update the current actor's state and behavior
Add sent message-events to the message-queue
Discard the current event
Make actor ready for the next event
eq instruction
Input
Instruction
Output
Description
u
eqv
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
Remove u from the stack
If u and v are the same raw value
Push #t onto the stack
Otherwise
Push #f onto the stack
if instruction
Input
Instruction
Output
Description
bool
ifT [F]
—
if bool is not falsy*, continue T (else F)
bool
if_notF [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
Remove bool from the stack
If bool is "falsy"
Continue execution at f-instr
Otherwise
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)
#?
#?
Remove k from the stack
If k is an #instr_t
Continue execution at k
Otherwise
Signal an error (E_NOT_EXE)
msg instruction
Input
Instruction
Output
Description
—
msg0
msg
copy event message to stack
—
msgn
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
Let msg be the entire message
Push nth(n, msg) onto the stack
nth instruction
Input
Instruction
Output
Description
v₁,…,vₙ,tailₙ
nthn
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
No effect
T (type)
X (op)
Y (imm)
Z (k)
#instr_t
+19 (nth)
positive
instr
Remove list from the stack
Let n be positive-1
Let scan be list
While n > 0
Let scan become cdr(scan)
Let n become n-1
Push car(scan) onto the stack
T (type)
X (op)
Y (imm)
Z (k)
#instr_t
+19 (nth)
negative
instr
Remove list from the stack
Let n be negative+1
Let scan be list
While n < 0
Let scan become cdr(scan)
Let n become n+1
Push cdr(scan) onto the stack
pair instruction
Input
Instruction
Output
Description
… tailhead
pairn
pair
create pair(s) from head and tail (n times)
Create a pair-list from some number of stack items.
Push an immediate value onto the top of the stack.
T (type)
X (op)
Y (imm)
Z (k)
#instr_t
+2 (push)
any
instr
Push value onto the stack
quad instruction
Input
Instruction
Output
Description
T
quad1
quad
create quad [T, #?, #?, #?]
XT
quad2
quad
create quad [T, X, #?, #?]
YXT
quad3
quad
create quad [T, X, Y, #?]
ZYXT
quad4
quad
create quad [T, X, Y, Z]
quad
quad-1
T
extract 1 quad field
quad
quad-2
XT
extract 2 quad fields
quad
quad-3
YXT
extract 3 quad fields
quad
quad-4
ZYXT
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.
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
Let sponsor be a new sponsor with quotas:
memory = 0
events = 0
cycles = 0
Push sponsor onto the stack
T
X (op)
Y (imm)
Z (k)
#instr_t
+8 (sponsor)
+1 (memory)
instr
Remove n from the stack
If n < 0
Signal an error (E_BOUNDS)
Otherwise
If the current sponsor has less than n memory available
Signal an error (E_MEM_LIM)
Otherwise
Subtract n to current sponsor memory
Add n to sponsor memory
T
X (op)
Y (imm)
Z (k)
#instr_t
+8 (sponsor)
+2 (events)
instr
Remove n from the stack
If n < 0
Signal an error (E_BOUNDS)
Otherwise
If the current sponsor has less than n events available
Signal an error (E_MSG_LIM)
Otherwise
Subtract n to current sponsor events
Add n to sponsor events
T
X (op)
Y (imm)
Z (k)
#instr_t
+8 (sponsor)
+3 (cycles)
instr
Remove n from the stack
If n < 0
Signal an error (E_BOUNDS)
Otherwise
If the current sponsor has less than n cycles available
Signal an error (E_CPU_LIM)
Otherwise
Subtract n to current sponsor cycles
Add n to sponsor cycles
T
X (op)
Y (imm)
Z (k)
#instr_t
+8 (sponsor)
+4 (reclaim)
instr
Add memory quota from sponsor to current sponsor (saturating)
Add events quota from sponsor to current sponsor (saturating)
Add cycles quota from sponsor to current sponsor (saturating)
Set memory, events, and cycles quota in sponsor to 0
T
X (op)
Y (imm)
Z (k)
#instr_t
+8 (sponsor)
+5 (start)
instr
Remove control from the stack
If control is a #actor_t
Set signal in sponsor to control (runable)
Remove sponsor from the stack
Otherwise
Signal an error (E_NOT_CAP)
T
X (op)
Y (imm)
Z (k)
#instr_t
+8 (sponsor)
+6 (stop)
instr
sponsor_reclaim(sponsor)
Set signal in sponsor to 0 (stopped)
Remove sponsor from the stack
state instruction
Input
Instruction
Output
Description
—
state0
state
copy actor state to stack
—
staten
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
Let state be the current actor's state
Push nth(n, state) onto the stack
typeq instruction
Input
Instruction
Output
Description
v
typeqT
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).