Skip to content

ABI

Frozen layouts and identifiers shared by generated code and the runtime.

Updated View as Markdown

The runtime ABI is the byte-level contract shared by generated WebAssembly, atoll-runtime-abi, and the host runtime. A field rename in Rust is harmless; changing a size, offset, tag, or interpretation requires coordinated ABI evolution.

Generated constants and compile-time layout assertions are authoritative. Older design documents are not.

Address types

Type Representation Meaning
UnitId u32 program-wide compiled Unit
GroupId u32 link-group artifact
MethodId u16 method within a Unit
FunctionId u32 dense continuation function in one build
FrameOffset u32 arena-relative frame handle
TCB handle u32 arena-relative task handle

Offset 0 is null. Function IDs are build-local and are not stable migration identities. The 32-bit offset space gives one arena a 4 GiB representational ceiling.

Frame header

FrameHeader is exactly 32 bytes with eight-byte alignment:

Offset Field Type Meaning
0 caller_cont 8 bytes caller continuation pair
8 pc u32 packed block and operation position
12 result_tag u32 tagged completion state
16 result_value u64 result payload lane
24 parent_cont u32 parent continuation handle
28 call_link u32 physical call-link state

The packed program counter is:

pc = (block_id << 16) | operation_index

call_link values are:

Value Meaning
0 resumed
1 present in physical call stack
2 completed

Debug stack reconstruction uses the continuation links plus compiler metadata; it must tolerate optimized or missing source frames.

Task block

The task control block is exactly 80 bytes with eight-byte alignment:

Offset Field Type
0 parent u32
4 first_child u32
8 next_sibling u32
12 cancelled u32
16 completed u32
20 completed_tag u32
24 debug_stamp u32
28 last_cont_fn u32
32 last_cont_frame u32
36 pending_io u32
40 pending_io_aux u32
44 ready_loop_head u32
48 ready_loop_tail u32
52 ready_resume_head u32
56 ready_resume_tail u32
60 awaiter_head u32
64 detached u32
68 frame_resident u32
72 completed_value i64

The two pending-I/O words are retained ABI slots but are vestigial in the current slot-based host I/O path.

debug_stamp changes when a TCB offset is reused. Debuggers and external task references use (TCB offset, debug_stamp), not the offset alone.

Scheduler nodes

Structure Size Purpose
ready-resume node 24 bytes TCB, function, frame, and result
awaiter node 24 bytes task completion waiter
ready-loop node 12 bytes loop/yield continuation
select group variable embeds coordinated awaiters

All links are arena-relative. Node layouts are internal ABI contracts and should be constructed through generated/runtime helpers.

Streams

A stream control block begins with:

Offset Field Type
0 tag u32
4 consumer awaiter u32
8 head u32
12 tail u32
16 count u32
20 capacity u32
24 closed u32
28 producer awaiter u32
32 data begins flex

The low handle bit distinguishes stream handles from naturally aligned task handles. Consumers must mask and validate the tag before resolving an offset.

Results

Value Constant
0 OK
1 ERR
2 CANCELLED
3 TIMEOUT
4 ERR_FLAT

The 64-bit result-value lane may contain a scalar, packed fields, or an arena-relative handle according to the operation contract.

Method entry

The v1 compiled method signature is:

(payload_abs: u32, payload_len: u32, root_tcb: u32) -> ()

The payload is copied into the selected Unit instance’s arena before entry. payload_abs is an absolute address valid for that entry path; root_tcb is body-relative. The method publishes completion through the root task state and runtime scheduler rather than a direct Wasm return value.

Arena globals

Before entering generated Unit code, the runtime selects the Unit’s link group and publishes the active arena base, capacity, allocator functions, and collector tables expected by that group. Growth republishes them after relocation.

Generated code must rematerialize absolute addresses after any operation that can relocate the arena. The arena generation is available to detect stale materialization.

Evolution

An ABI change needs all of the following:

  1. update the canonical ABI crate and generated constants;
  2. update compiler lowering and runtime reads/writes together;
  3. change compatibility metadata or layout fingerprints;
  4. add compile-time size and offset assertions;
  5. test old/new redeploy and migration rejection;
  6. update this page and remove conflicting legacy claims.

Adding a Rust field without assigning an ABI offset does not extend the wire layout. Reordering a repr(C) structure without a coordinated ABI version is a breaking change.

Invariants

  • every offset table is little-endian and byte-exact;
  • generated and host code use the same size and alignment constants;
  • null offsets are never dereferenced;
  • build-local function IDs do not cross incompatible redeploys;
  • TCB debug identity includes its reuse stamp;
  • reserved or vestigial fields retain their offsets until an ABI version explicitly removes them.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close