Skip to main content

Crate bytecode_lang

Crate bytecode_lang 

Source
Expand description

§bytecode_lang

LSB, the LexerSketch bytecode format: one register-machine instruction set for every language and every execution tier, its module model, a versioned binary encoding, a budgeted decoder, a deterministic disassembler, and a builder that resolves branch labels.

The VM (bvm-lang 2.0) executes LSB, code generators (codegen-lang 2.0) emit it, program images (loader-lang) store it, and the debugger (dap-lang) reads its line tables. This crate owns the format only; it executes nothing.

§Quick start

Build a function, encode the module, decode it back, and read the listing:

use bytecode_lang::{decode, disassemble, encode, Inst, IntOp, IntTy, ModuleBuilder, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("add", &[ValType::I64, ValType::I64], &[ValType::I64]);
let (a, b) = (f.param(0), f.param(1));
let sum = f.reg(ValType::I64);
f.emit(Inst::IAdd { dst: sum, lhs: a, rhs: b, op: IntOp::new(IntTy::I64) });
f.ret(sum);
m.add_function(f)?;
let module = m.finish()?;

let bytes = encode(&module);
let back = decode(&bytes)?;
assert_eq!(back, module);
assert!(disassemble(&back).contains("iadd.i64 r2, r0, r1"));

§The model

  • Registers, typed. Every function has a frame of 64-bit registers, each with a declared ValType: unboxed scalars for static languages, ValType::Dyn for dynamic values, and references. Declared types make GC roots exact and let the verifier check every operand.
  • Eight-byte instructions. Inst is a Copy enum of eight bytes; a function body is a flat &[Inst] that an interpreter walks without re-parsing. Branch targets are resolved instruction indices.
  • Policies on every operation. Integer instructions carry an IntOp (the operand type plus the overflow, division-by-zero, and shift policies), dynamic ones a Policy, float-to-integer conversions a FloatConv, so every tier computes the same result or raises the same ErrorKind.
  • Dynamic languages built in. dyn arithmetic and comparisons with a numeric fast path, ordered hash maps, property access, iteration, and per-module Hooks that let a language supply its own semantics for the slow path. overflow = promote gives PHP’s int-to-float arithmetic, shift = saturate its shifts.
  • PHP’s value and reference semantics. dsep_index separates a nested array only when it may be shared; references into map slots and properties (dref_index, dbind_index) are transparent in their slots and survive copies as PHP’s do.
  • Dynamic calls with real signatures. A function or import may carry a ParamList (names, rest parameters, defaults, by-reference parameters); dcall_shape passes named and spread arguments through a CallShape, and ParamList::bind is the one binding rule every tier applies.
  • Coroutines. Stackful coroutines (coro_new, yield, await, resume, spawn) for generators, fibers, and async tasks; see CoroState.

The normative description of every instruction is specs/LSB.md in the LexerSketch plan; docs/API.md documents every public item.

§Guarantees

  • decode never panics on any input, never recurses, and allocates no more than its Limits and the input length justify.
  • decode(&encode(&m)) == Ok(m) for every module, and encode(&decode(bytes)?) == bytes for every accepted input: each module has exactly one encoding.
  • encode and disassemble are deterministic.
  • A function produced by ModuleBuilder::add_function has no unresolved or out-of-range branch target.

What this release does not do: verify a module’s meaning (index ranges, register types, frame shapes). That is the v0.5 verifier, whose rules are specified in specs/LSB.md §8. Until then a decoded module is well-formed but unverified.

Structs§

Binding
The result of ParamList::bind: what each parameter receives, and the presence mask.
CallShape
The argument layout of a dcall_shape call site: one entry per window register (dst+1 ..= dst+len), saying whether that argument is positional, named, or spread. Shapes live in a per-function table and are named by a ShapeId.
ConstId
An index into the module’s constant pool.
DecodeError
Why bytes are not a module, and where.
Export
A named item the module exports: the shared module interface the loader links native and bytecode units through.
Field
A named, typed field of a StructDef.
FieldIdx
A field slot of a struct type, counted across the whole parent chain (a struct’s fields start with its parent’s).
FieldSpec
One operand of an opcode: its field name, its kind, and its slot.
FloatConv
The modifier byte of Inst::F32ToInt and Inst::F64ToInt: the destination integer type and the float_to_int policy for NaN and out-of-range values.
FuncId
An index into the module’s function table.
FuncType
A function signature.
Function
One function of a module: its frame layout, its tables, its code, and its debug information.
FunctionBuilder
Builds one function. Created by ModuleBuilder::function, added back with ModuleBuilder::add_function.
Global
A module-level variable.
GlobalId
An index into the module’s global table.
Handler
An exception-handling region: an error raised by an instruction at a pc in start..end transfers control to target with the error value in catch.
HookBinding
One bound Hook. A module binds each hook at most once; bindings are kept sorted by hook code.
Import
A host function the module calls by index (Inst::CallImport), bound by the loader by module and name.
ImportId
An index into the module’s import table (host functions).
IntConv
The modifier byte of Inst::IntCast: source type, destination type, and the overflow policy for values that do not fit.
IntOp
An integer type plus the policies integer arithmetic consults: the modifier byte of every typed integer instruction that computes an integer.
IntPair
The modifier byte of the bit-level conversions (Zext, Sext, Trunc), which never raise an error and so carry no policy.
JumpTable
A jump table of Inst::Switch: targets[v] for a selector v in range, default otherwise.
Label
A position in a function’s code, named before it is known.
Limits
The budgets the decoder enforces on untrusted input.
LineRow
A row of a function’s line table: instructions from pc up to the next row’s pc came from this source position.
LocalVar
A named source variable living in a register over a pc range, for debuggers.
Method
A named method of a StructDef: the function receives the instance as its first parameter.
Module
A complete LSB module: the unit that is encoded, decoded, verified, loaded, and disassembled.
ModuleBuilder
Builds a Module.
NameRef
An index into the running function’s name list, which maps it to a StrId.
Param
One parameter of a ParamList.
ParamList
The dynamic-call signature of a function or an import: what lets a dcall or dcall_shape pass named, spread, extra, missing, and by-reference arguments (specs/LSB.md §5.15).
Policy
The complete OPS policy set, packed into six bits: the modifier byte of every dynamic arithmetic instruction (dadd, dshl, dpow, …).
Reg
A register in the running function’s frame.
ShapeId
An index into the running function’s call shapes (see Inst::DCallShape and CallShape): which window arguments are positional, named, or spread. 16-bit, like NameRef, so a function may use 65,536 distinct shapes.
StrId
An index into the module’s string table.
StructDef
A struct (record) type.
TableId
An index into the running function’s jump tables (see Inst::Switch).
Target
An instruction index within one function: the destination of a branch.
TypeId
An index into the module’s type table.
TypeRef
An index into the running function’s type list, which maps it to a TypeId.
UpvalIdx
A captured value of the running closure (see Inst::GetUpval).

Enums§

ArgItem
One argument of a dynamic call, as ParamList::bind sees it.
ArgKind
One argument of a CallShape.
BindError
Why a dynamic call’s arguments do not bind (ParamList::bind). A VM raises ArgumentError (E0114) for every variant; the variant is the detail a diagnostic can show.
Bound
What one parameter received from ParamList::bind.
BuildError
Why a function or module could not be built.
Callee
A function the module can call: one of its own, or an import.
Const
An entry of the module’s constant pool.
CoroState
The state of a coroutine, as Inst::CoroStatus reports it (a u8 code).
DecodeErrorKind
What is wrong with the input; see DecodeError.
DivZero
The div_zero policy: what integer div, rem, floor_div, and floor_mod do when the divisor is zero.
ErrorKind
A runtime error kind with its stable code.
ExportItem
What an Export makes visible to other units.
FieldKind
What an operand is: the type of one Inst field.
FloatToInt
The float_to_int policy: what float-to-integer conversion does with NaN or an out-of-range value.
FloatTy
A floating-point type (IEEE 754 binary32 or binary64).
Hook
A dynamic-operation hook: the function a dynamic instruction calls when its built-in fast path does not apply (for example dadd on two strings, or get_prop on an object with no such field).
Inst
One LSB instruction, decoded.
InstError
Why eight bytes are not an instruction.
IntTy
An integer type: the width and signedness an integer instruction works at (OPS §1).
Kind
The kind of a dynamic value: what Inst::TypeOf returns and Inst::IsKind tests.
Limit
Which budget a DecodeErrorKind::LimitExceeded refers to.
Opcode
The operation an Inst performs: its first encoded byte.
Overflow
The overflow policy: what integer add, sub, mul, neg, abs, signed MIN / -1, and int_cast do when the exact result does not fit.
ParamError
Why a ParamList is malformed, or does not fit the signature it is attached to.
ParamKind
How a parameter of a ParamList receives arguments.
Prim
A static representation crossing the dyn boundary: the modifier of Inst::ToDyn and Inst::FromDyn.
ShapeError
Why a CallShape is malformed.
Shift
The shift policy: what shl and shr do when the amount is at least the bit width, or negative for a signed amount.
Slot
Where an operand lives in the eight-byte instruction word.
TypeDef
An entry of the module’s type table.
ValType
The declared type of a register, a global, a field, or a parameter.

Constants§

FORMAT_VERSION
The format version this crate writes and the only one it reads.
MAGIC
The four bytes every encoded module starts with: LSB\0.

Functions§

decode
Decodes a module with the default Limits.
decode_with
Decodes a module with explicit budgets.
disassemble
The module’s disassembly: a deterministic text listing of every table and every function, with branch targets as labels and names resolved in comments.
encode
Encodes a module into its canonical bytes.