This document details the core runtime structures of the Ribbon Virtual Machine (RVM). While the Isa document specifies the instructions the RVM executes, this document describes the state and memory layout of the execution environment itself.

The central component of the RVM is the Fiber.

Contents

The Fiber: A Lightweight Execution Context

A Fiber encapsulates all the state required for an independent thread of execution within the RVM. Unlike OS threads, fibers are managed entirely in user space, making them extremely lightweight and efficient to create, manage, and switch between. A single OS thread can manage thousands of fibers.

Each Fiber is a single, contiguous block of memory, which allows for very fast allocation and deallocation and improves data locality.

Memory Layout

The memory for a Fiber is laid out sequentially to ensure no padding is needed between sections. The total size is typically only a few megabytes.

  1. FiberHeader: A fixed-size structure at the beginning of the memory block that contains pointers and stack-tops for all other sections. It acts as the control panel for the fiber.
  2. Register Stack Block: The memory region for the RegisterStack.
  3. Data Stack Block: The memory region for the DataStack.
  4. Call Stack Block: The memory region for the CallStack.
  5. Set Stack Block: The memory region for the SetStack.

This contiguous layout is a key design feature, enabling high performance and simplifying the VM’s implementation.

The Fiber Header: The Control Panel

The FiberHeader is the entry point to a fiber’s state. It contains the top pointers for all stacks and the necessary metadata for execution.

Stacks

The RVM uses several distinct stacks, each with a specific purpose. These are simple stack allocators that manage regions of the fiber’s contiguous memory block.

Effect Handling Machinery

The RVM’s static guarantees for Algebraic Effects are supported by two key runtime structures in the FiberHeader:

Execution Model

The core of the VM is a dispatch loop that executes the instruction pointed to by the ip of the top-most CallFrame.

  1. Fetch: The VM reads the 64-bit word at the ip.
  2. Decode: The word is split into a 16-bit opcode and 48 bits of operand data.
  3. Dispatch: The opcode is used to jump to the logic for that instruction.
  4. Execute: The instruction logic is performed, modifying the Fiber’s state (e.g., writing to registers, changing the ip, pushing to stacks).
  5. Loop: The ip is advanced (unless it was a jump/branch instruction), and the process repeats.

This loop continues until a halt instruction is reached, an error occurs, or (in debugging mode) a breakpoint is hit.

Function Calls

The call, call_c, and prompt instructions follow a similar procedure to create a new stack frame:

  1. A new CallFrame is pushed onto the CallStack. Its function pointer is set to the target function.
  2. The output register is recorded in the new frame, indicating where the return value should be placed in the caller’s register set.
  3. A new RegisterArray is pushed onto the RegisterStack and its pointer is stored in the new CallFrame’s vregs field.
  4. Arguments are copied from the caller’s registers into the new register frame.
  5. The ip in the new CallFrame is set to the entry point of the target function.
  6. Execution transfers to the new frame.

Returning and Cancelling