178 lines
5.0 KiB
Markdown
178 lines
5.0 KiB
Markdown
# Host ABI and Syscalls
|
|
|
|
Domain: host ABI structure
|
|
Function: normative
|
|
|
|
This chapter defines the structural ABI between the PVM and the host environment.
|
|
|
|
It focuses on:
|
|
|
|
- syscall identity;
|
|
- load-time resolution;
|
|
- instruction-level call semantics;
|
|
- metadata shape;
|
|
- argument and return-slot contract.
|
|
|
|
Operational policies such as capabilities, fault classes, determinism, GC interaction, budgeting, and blocking are split into a companion chapter.
|
|
|
|
## 1 Design Principles
|
|
|
|
The syscall ABI follows these rules:
|
|
|
|
1. **Stack-based ABI**: arguments and return values are passed through VM slots.
|
|
2. **Canonical identity**: host services are named by stable `(module, name, version)`.
|
|
3. **Load-time resolution**: symbolic host bindings are resolved before execution.
|
|
4. **Metadata-driven execution**: arity and result shape come from syscall metadata.
|
|
5. **Not first-class**: syscalls are callable but not ordinary function values.
|
|
|
|
## 2 Canonical Syscall Identity
|
|
|
|
Syscalls are identified by a canonical triple:
|
|
|
|
```
|
|
(module, name, version)
|
|
```
|
|
|
|
Example:
|
|
|
|
```
|
|
("gfx", "present", 1)
|
|
("audio", "play", 2)
|
|
```
|
|
|
|
This identity is:
|
|
|
|
- language-independent;
|
|
- toolchain-stable;
|
|
- used for linking and capability gating.
|
|
|
|
Input queries are VM-owned intrinsic calls in v1 and are outside the syscall identity space.
|
|
|
|
## 3 Syscall Resolution
|
|
|
|
The host maintains a registry:
|
|
|
|
```
|
|
(module, name, version) -> syscall_id
|
|
```
|
|
|
|
At load time:
|
|
|
|
1. the cartridge declares required syscalls by canonical identity;
|
|
2. bytecode encodes host-backed call sites as `HOSTCALL <sysc_index>`;
|
|
3. the loader validates and resolves those identities;
|
|
4. the loader rewrites `HOSTCALL <sysc_index>` into `SYSCALL <id>`;
|
|
5. the executable image uses only numeric syscall ids at runtime.
|
|
|
|
Raw `SYSCALL <id>` is not valid in a PBX pre-load artifact and must be rejected there.
|
|
|
|
## 4 Syscall Instruction Semantics
|
|
|
|
Pre-load artifact form:
|
|
|
|
```
|
|
HOSTCALL <sysc_index>
|
|
```
|
|
|
|
Final executable form:
|
|
|
|
```
|
|
SYSCALL <id>
|
|
```
|
|
|
|
Where:
|
|
|
|
- `<sysc_index>` indexes the program-declared syscall table;
|
|
- `<id>` is the final numeric host syscall id.
|
|
|
|
Execution steps:
|
|
|
|
1. the VM looks up syscall metadata by `<id>`;
|
|
2. the VM ensures the argument contract is satisfiable;
|
|
3. the syscall executes in the host environment;
|
|
4. the syscall produces exactly the declared `ret_slots`.
|
|
|
|
## 5 Syscall Metadata Table
|
|
|
|
Each syscall is defined by metadata.
|
|
|
|
Conceptual structure:
|
|
|
|
```
|
|
SyscallMeta {
|
|
id: u32
|
|
module: string
|
|
name: string
|
|
version: u16
|
|
arg_slots: u8
|
|
ret_slots: u8
|
|
capability: CapabilityId
|
|
may_allocate: bool
|
|
cost_hint: u32
|
|
}
|
|
```
|
|
|
|
Fields:
|
|
|
|
| Field | Description |
|
|
| -------------- | ------------------------------------------------ |
|
|
| `id` | Unique numeric syscall identifier |
|
|
| `module` | Canonical module name |
|
|
| `name` | Canonical syscall name |
|
|
| `version` | ABI version of the syscall |
|
|
| `arg_slots` | Number of input stack slots |
|
|
| `ret_slots` | Number of return stack slots |
|
|
| `capability` | Required capability |
|
|
| `may_allocate` | Whether the syscall may allocate VM heap objects |
|
|
| `cost_hint` | Expected cycle cost |
|
|
|
|
The loader uses this table to resolve identities and validate declared ABI shape.
|
|
|
|
## 6 Arguments and Return Values
|
|
|
|
Syscalls use the same slot-oriented argument/return philosophy as ordinary calls.
|
|
|
|
### Argument passing
|
|
|
|
Arguments are pushed before the syscall.
|
|
|
|
Example:
|
|
|
|
```
|
|
push a
|
|
push b
|
|
SYSCALL X
|
|
```
|
|
|
|
### Return values
|
|
|
|
After execution, the syscall leaves exactly `ret_slots` values on the stack.
|
|
|
|
Composite results use multiple stack slots rather than implicit hidden structures.
|
|
|
|
Return shape must also follow the operational policy in [`16a-syscall-policies.md`](16a-syscall-policies.md):
|
|
|
|
- operations with observable operational failure paths should expose an explicit `status:int` return;
|
|
- operations with no real operational error path may remain `void` (`ret_slots = 0`);
|
|
- stack shape remains strict in both cases and must match syscall metadata exactly.
|
|
|
|
## 7 Syscalls as Callable Entities (Not First-Class)
|
|
|
|
Syscalls behave like call sites, not like first-class guest values.
|
|
|
|
This means:
|
|
|
|
- syscalls can be invoked;
|
|
- syscalls cannot be stored in variables;
|
|
- syscalls cannot be passed as function values;
|
|
- syscalls cannot be returned as closures.
|
|
|
|
Only user-defined functions and closures are first-class.
|
|
|
|
## 8 Relationship to Other Specs
|
|
|
|
- [`16a-syscall-policies.md`](16a-syscall-policies.md) defines operational policies layered on top of this ABI.
|
|
- [`15-asset-management.md`](15-asset-management.md) defines asset-domain semantics behind some syscall families.
|
|
- [`08-save-memory-and-memcard.md`](08-save-memory-and-memcard.md) defines persistent save-domain semantics.
|
|
- [`02a-vm-values-and-calling-convention.md`](02a-vm-values-and-calling-convention.md) defines the slot/call philosophy reused by the syscall ABI.
|