Documentation
¶
Overview ¶
Package bytecode defines the instruction set of the Kukel stack machine and the compiled program image.
An instruction is a one-byte opcode followed by fixed-width little-endian operands: u16 for indices (constants, slots, globals, argument counts) and u32 for jump targets (absolute offsets in the function's code).
Index ¶
- Constants
- func Disassemble(w io.Writer, p *Program) error
- func Encode(p *Program) ([]byte, error)
- func EncodeModule(m *Module) ([]byte, error)
- func LocalName(name string) string
- func ReadU16(code []byte, at int) int
- func ReadU32(code []byte, at int) int
- func Relocate(f *Function, global, class, fn func(int) (int, error)) error
- func StackUse(op Op, operands []int) (pops, pushes int)
- func Verify(p *Program) error
- type Class
- type Function
- type Global
- type GlobalKind
- type JumpTable
- type Layout
- type LineInfo
- type LocalInfo
- type Method
- type Module
- type Op
- type OpInfo
- type Program
- type Upvalue
Constants ¶
const ( CmpLt = iota CmpLe CmpGt CmpGe CmpEq CmpNe CmpJumpIfTrue = 0x100 )
The comparison operand of CMP_JUMP: one of these, plus CmpJumpIfTrue to jump when the comparison holds (else when it does not).
const ( MaxCodeSize = 1 << 24 MaxStackDepth = 1 << 20 )
MaxCodeSize and MaxStackDepth are the largest code size (bytes) and MaxStack of one function a .kkc file can hold; the compiler refuses bigger functions.
const FormatVersion = 11 // 2: milestone 12 superinstructions; 3: native modules (GlobalNative); 4: THROW takes only Error objects (Kukel v0.4); 5: jump tables and ADD_LOCAL (Kukel v0.8); 6: native base classes, CALL_NATIVE (Kukel v0.9); 7: ARITH_LK, CMP_JUMP_LK (Kukel v0.22); 8: INDEX_LL and the other fused forms of Kukel v0.26; 9: generators, GENERATOR, YIELD and ITER_CLOSE (Kukel v0.34); 10: GEN_RETURN (Kukel v0.35: a generator steps in the loop that walks it); 11: YIELD_VALUE and ITER_SEND (Kukel v0.37: send and yield from)
FormatVersion is the version of the .kkc format written by Encode.
Variables ¶
This section is empty.
Functions ¶
func Disassemble ¶
Disassemble writes a readable listing of the program.
func EncodeModule ¶ added in v0.7.0
EncodeModule serializes a module.
func LocalName ¶ added in v0.2.0
LocalName returns the last part of a qualified name: "Stack" for "util::Stack". A class's constructor is the member function with the local name of the class.
func ReadU16 ¶
ReadU16 reads a little-endian u16 operand (one bounds check, where binary.LittleEndian with a re-slice needs two: it is in every instruction with an operand).
func Relocate ¶ added in v0.7.0
Relocate rewrites the global, class and function index operands of f's code with global, class and fn. A mapping can refuse an index with an error.
func StackUse ¶
StackUse returns how many values an instruction pops and pushes. For ITER_NEXT it is the path that continues the loop (the end of the loop pushes nothing); TRY_PUSH's handler starts with one more value than the instruction had.
func Verify ¶
Verify checks that a program cannot make the VM crash: every index in an instruction refers to something that exists, jumps land on instructions, no function can run past its last instruction, and on every path the operand stack depth is the same at each instruction, never negative and never above the function's MaxStack. Decode calls it for every file; the compiler's output always passes. Try handlers are counted the same way: the same number at each instruction on every path, TRY_POP only with one registered, and none left when the function returns.
Types ¶
type Class ¶
type Class struct {
Name string
Base int // index in Program.Classes, or -1
Native string // a native base class (Base is -1): its qualified name ("strings::Builder")
Fields []string // own instance data members (the base's come first in an object)
Statics []string // own static data members
Methods []Method // own member functions
}
Class describes a class. A base class always comes before the classes derived from it in Program.Classes.
type Function ¶
type Function struct {
FuncName string
File string // source file
Line int // line of the definition
NumParams int // required parameters
NumOptional int // optional parameters (with a default), after the required ones
HasRest bool // a rest parameter follows the optional ones
NumSlots int // parameters + locals + hidden slots (iterators, saved values)
MaxStack int // operand stack depth needed above the slots
Code []byte
Consts []value.Value
Lines []LineInfo // sorted by PC
SlotNames []string // for the disassembler ("i/w": a slot two blocks share)
Upvalues []Upvalue // a function literal's captured variables
NumCaches int // inline cache slots of member instructions (the VM keeps the caches)
Tables []JumpTable // of SWITCH_TABLE instructions
// Locals are the named parameters and locals with the code where each
// is in scope, for the debugger. Only the compiler sets them (not in
// files: the debugger runs from source).
Locals []LocalInfo
// Saved holds the opcodes that BREAK instructions replaced, by pc: only
// in a copy of the code the debugger made (never in files).
Saved map[int]Op
// Index is the function's position in Program.Funcs (-1 for Init). It is
// not stored in files: the compiler and Decode set it.
Index int
}
Function is a compiled function: its code and constants.
func (*Function) ParamSlots ¶
ParamSlots is the number of slots that hold parameters.
type Global ¶
type Global struct {
Name string
Kind GlobalKind
Func int // for GlobalFunc
Class int // for GlobalClass
}
Global describes one global slot.
type GlobalKind ¶
type GlobalKind uint8
GlobalKind says what a global slot holds when the program starts.
const ( GlobalVar GlobalKind = iota // a global variable (nil until initialized) GlobalFunc // a function: Funcs[Func] GlobalBuiltin // a built-in, looked up by name GlobalClass // a class: Classes[Class] GlobalNative // a function or class of a native module, looked up by its qualified name )
type JumpTable ¶ added in v0.8.0
JumpTable maps the ints Min, Min+1, ... to code offsets (-1: none, the instruction's miss target).
type Layout ¶ added in v0.7.0
Layout is the data members of a class, which the code of member functions and of static data member accesses refers to by index: a program that links a module must have the same.
type LineInfo ¶
type LineInfo struct {
PC int
Line int
// Cont: the code continues the line, it does not start it (a switch's
// dispatch, after the body): the debugger does not stop there. Only
// the compiler sets it (not in files: the debugger runs from source).
Cont bool
}
LineInfo maps code from PC onwards to a source line.
type LocalInfo ¶ added in v0.27.0
LocalInfo is a parameter or local: slot Slot holds it in the code from Start (after its initialization) to End (the end of its block).
type Method ¶
type Method struct {
Name string
Static bool
Func int // index in Program.Funcs, or -1 when declared but not defined
}
Method is a member function of a class.
type Module ¶ added in v0.7.0
type Module struct {
Name string
Header string // its declarations, with empty function bodies and no initializers (Kukel source)
Globals []Global // what global indexes in Code stand for: Name and Kind
Classes []string // what class indexes stand for: class names
Layouts []Layout // the classes' data members when the module was compiled
Funcs []string // the functions of others, after Code in the index space
Code []*Function // the module's own functions
Init int // the function in Code that initializes its global variables
}
Module is a module compiled on its own (kukel build -module), for linking into programs that use it. Its code refers to globals, classes and other modules' functions by index into the name tables here; the compiler that links it finds them by name and relocates the code.
The module file (Encode's format with flags 1):
"KUKL", uv format version, uv flags (1)
str name, str header
uv n, globals str name, byte kind
uv n, classes str name, uv n + str data members (with the
inherited ones), uv n + str static data members
uv n, functions str name: functions of other modules (and of
the prelude) the code calls, as Code index len(Code)+i
uv init index in Code
uv n, code functions
u32 CRC-32 (IEEE) of everything before it
func DecodeModule ¶ added in v0.7.0
DecodeModule reads a module written by EncodeModule. Its code is checked when it is linked (Relocate, and Verify of the program).
type Op ¶
type Op uint8
Op is an opcode.
const ( OpNop Op = iota // Stack. OpConst // k u16: push Consts[k] OpNil OpTrue OpFalse OpPop OpDup OpDup2 // a b -> a b a b // Variables. Stores pop the value. OpLoadLocal // slot u16 OpStoreLocal // slot u16 OpLoadGlobal // global u16 OpStoreGlobal // global u16 // Arithmetic and logic: pop operands, push the result. OpAdd OpSub OpMul OpDiv OpMod OpBitAnd OpBitOr OpBitXor OpShl OpShr OpNeg OpNot // push !truthy(x) OpBitNot OpEq OpNe OpLt OpLe OpGt OpGe OpInc // x + 1 (for ++) OpDec // x - 1 (for --) // Control. OpJump // target u32 OpJumpIfFalse // target u32: pop, jump if not truthy OpJumpIfTrue // target u32: pop, jump if truthy OpJumpIfArg // n u16, target u32: jump if argument n was passed (n < argc) // Calls. The callee is below its arguments on the stack. OpCall // argc u16 OpCallV // call with the arguments in a vector on top of the callee OpReturn // return the top of the stack OpReturnNil // return nil // Vectors: vector literals and the argument lists of spread calls. OpMakeVector // n u16: pop n values, push a vector of them OpAppend // pop x, append it to the vector below OpExtend // pop vector x, append its elements to the vector below // Collections. OpMakeMap // n u16: pop n key/value pairs, push a map of them OpIndexGet // x i -> x[i] OpIndexSet // x i v -> v, after x[i] = v OpIterInit // x -> an iterator over x OpIterNext // slot u16, n u16, end u32: advance the iterator in slot; push n values, or jump to end // Classes and objects. Member names are string constants (u16 index). OpLoadThis // push this OpLoadMember // field u16: push this.fields[field] OpStoreMember // field u16: pop into this.fields[field] OpGetMember // name u16, cache u16: obj -> obj->name (data member or bound method) OpSetMember // name u16, cache u16: obj v -> v, after obj->name = v OpCallMethod // name u16, argc u16, cache u16: obj args -> result of obj->name(args) OpCallMethodV // name u16, cache u16: obj argvector -> result OpCallDirect // func u16, argc u16: obj args -> result of Funcs[func] with this = obj (Base::m(...)) OpLoadFunc // func u16: push Funcs[func] (a static member function) OpLoadStatic // class u16, slot u16: push a static data member OpStoreStatic // class u16, slot u16: pop into a static data member OpNew // argc u16: class args -> new object (the constructor runs) OpNewV // class argvector -> new object // Exceptions. OpTryPush // kind u16, handler u32: register a handler (kind 0: catch, 1: finally) OpTryPop // remove the innermost handler OpThrow // pop an Error object and throw it (anything else: throw a TypeError) OpRethrow // pop a pending exception (from a finally handler) and throw it again // Closures. A captured local lives in a cell held by its slot. OpNewCell // slot u16: pop a value into a new cell in the slot OpLoadCell // slot u16: push the value in the slot's cell OpStoreCell // slot u16: pop into the slot's cell OpLoadUpvalue // index u16: push the value of the closure's captured cell OpStoreUpvalue // index u16: pop into the closure's captured cell OpClosure // func u16: push a closure of Funcs[func] (see Function.Upvalues) // Superinstructions (milestone 12): common pairs fused into one. OpCmpJump // cmp u16, target u32: pop b, a; jump if (a cmp b) == want (see CmpJump*) OpIncLocal // slot u16: slot++ (a statement: no value is pushed) OpDecLocal // slot u16: slot-- // Jump tables (Kukel v0.8): a switch whose case values are dense ints. OpSwitchTable // table u16, miss u32: pop x; jump to Tables[table]'s target for x, else to miss OpAddLocal // slot u16: pop x; slot = slot + x (statement "local += x") // Native base classes (Kukel v0.9). OpCallNative // class u16, name u16, argc u16: this args -> result of the member function name of the native class in global class, on this (Base::m(...)) // The debugger (Kukel v0.15). OpBreak // the start of a source line in code the debugger copied: its hook, then the instruction Function.Saved holds for this pc // A local and a constant as the operands (Kukel v0.22): "n - 1", "n < 2". OpArithLK // op u16, slot u16, const u16: push slot op Consts[const]; op is one of ADD to SHR OpCmpJumpLK // cmp u16, slot u16, const u16, target u32: jump if (slot cmp Consts[const]) == want, as CMP_JUMP // More of the same (Kukel v0.26), for the patterns programs run most. OpIndexLL // x u16, i u16: push x[i], both locals: "v[i]" OpGetMemberL // slot u16, name u16, cache u16: push slot->name, as GET_MEMBER: "p->next" OpArithLL // op u16, slot u16, slot u16: push slot1 op slot2, as ARITH_LK: "a * b" OpCmpJumpLL // cmp u16, slot u16, slot u16, target u32: as CMP_JUMP_LK, with a local on the right: "i < n" OpArithK // op u16, const u16: x -> x op Consts[const]: "x * y + 1" OpCmpJumpK // cmp u16, const u16, target u32: x -> ; as CMP_JUMP, with Consts[const] on the right: "s[i] >= '0'" // Generators (Kukel v0.34). OpGenerator // return a generator that runs the rest of the function from the next instruction, a step at a time OpYield // pop x: the generator's step ends, giving x; the next step goes on after it OpIterClose // slot u16: close the generator a for-each loop walks with the iterator in slot (nothing for others) OpGenReturn // the generator ends (its return; and the end of its body) // send and yield from (Kukel v0.37). OpYieldValue // pop x: as YIELD, and the next step goes on with the value sent to it (send; nil for next) pushed OpIterSend // slot u16: pop x, the value the next step of the generator a for-each loop walks with the iterator in slot gets (yield from; dropped for others) )
type Program ¶
type Program struct {
Globals []Global
Classes []*Class
Funcs []*Function
Init *Function
Main int // global index of main
}
Program is a compiled program: global slots, functions, an init function that runs the global initializers, and the entry point.
func Debuggable ¶ added in v0.15.0
Debuggable gives a copy of p for the debugger: every function has its own copy of its code, in which the first instruction of each source line is a BREAK (not where code continues a line: LineInfo.Cont); Function.Saved keeps the instruction it replaced. p itself does not change, and the copy runs as p does where no debug hook is set (the VM then runs the saved instruction at once).