Skip to main content

Althread Virtual Machine

Virtual Machine State

The state of the virtual machine is represented by:

  • The values of global variables
  • The state of communication channels (messages in transit)
  • A structure for each process in execution. The state of an executing process includes its execution stack, i.e., the process's local variables, and the value of the instruction pointer, which is the index of the instruction currently being executed.

The execution stack of a process contains no debugging information; it only includes the values of local variables and intermediate expression results in the form of a Literal array. Expressions using local variables refer to the index of the variable in the process's execution stack. The index of a local variable is determined during compilation, enabling fast access during execution.

To simplify, global variables are stored in a HashMap (dictionary), and their values are directly accessible by name.

Instructions

The virtual machine's instructions are represented by an enum InstructionType. Each instruction contains fields with the information required for its execution:

Instruction Types

Empty

An empty instruction, does nothing.

Expression (LocalExpressionNode)

Evaluates an expression and pushes the result onto the stack. LocalExpressionNode is the root of a tree representing the expression.

Push (Literal)

Pushes the given literal onto the stack.

Unstack {unstack_len: usize}

Removes unstack_len values from the stack.

Destruct

Replaces the tuple at the top of the stack with its elements. A tuple with 3 elements will be replaced by 3 values on the stack.

GlobalReads {variables: Vec<String>, only_const: bool}

Pushes the values of the global variables onto the stack. The only_const field indicates whether all variables are constants (if so, the instruction can be optimized since it’s not global).

GlobalAssignment {identifier: String, operator: BinaryAssignmentOperator, unstack_len: usize}

Assigns the value at the top of the stack to the given global variable identifier and removes unstack_len values from the stack.

LocalAssignment {index: usize, operator: BinaryAssignmentOperator, unstack_len: usize}

Assigns the value at the top of the stack to the local variable at the given index index and removes unstack_len values from the stack.

Declaration {unstack_len: usize}

Declares a variable in the current scope, initialized with the value at the top of the stack, and removes unstack_len values from the stack.

RunCall {name: String, unstack_len: usize}

Starts a new thread executing the program name with the value at the top of the stack as an argument, then removes unstack_len values from the stack. Finally, adds the thread's PID to the stack.

FnCall {name: String, unstack_len: usize, variable_idx: Option<usize>, arguments: Option<Vec<usize>}>

Calls the function name with local arguments at the given indexes, then removes unstack_len values from the stack. If variable_idx is provided, the function is a method of the object at the given index. Finally, the function result is added to the stack, if it returns a value.

JumpIf {jump_false: i64, unstack_len: usize}

Jumps to instruction jump_false if the value at the top of the stack is false, then removes unstack_len values from the stack. The jump value is relative to the current instruction.

Jump {jump: i64}

Jumps to instruction jump. The jump value is relative to the current instruction.

Break {jump: i64, unstack_len: usize, stop_atomic: bool}

Jumps to instruction jump while removing unstack_len values from the stack. If stop_atomic is true, stops atomic execution.

ChannelPeek {channel_name: String}

Checks if a message is available in the channel channel_name. If so, adds the message and true to the stack; otherwise, adds false.

ChannelPop {channel_name: String}

Removes the message from the channel channel_name (does not add it to the stack).

WaitStart {dependencies: WaitDependency, start_atomic: bool}

Starts a wait on a condition using the given dependencies. If start_atomic is true, begins an atomic section. Dependencies include global variables or channels used in the condition. This instruction does not modify the virtual machine state and merely indicates the start of a waiting zone.

Wait {jump: i64, unstack_len: usize}

If the top of the stack is false, jumps to jump (relative to the next instruction); otherwise, proceeds to the next instruction. In both cases, removes unstack_len values from the stack.

Send {channel_name: String, unstack_len: usize}

Sends the value at the top of the stack to the channel channel_name, then removes unstack_len values from the stack.

Connect {sender_pid: Option<usize>, receiver_pid: Option<usize>, sender_channel: String, receiver_channel: String}

Connects sender_channel and receiver_channel between the processes sender_pid and receiver_pid. If sender_pid or receiver_pid is None, the current process is used. If a Send has already occurred, the message is directly transferred.

AtomicStart

Begins an atomic section where processes cannot be interrupted, preventing concurrency issues. An atomic section ends with AtomicEnd and must not contain wait instructions, except at the beginning.

AtomicEnd

Ends an atomic section.

EndProgram

Terminates the current process.

Exit

Terminates all processes.

The InstructionType enum is defined in the file /vm/src/instruction.rs.