The Burn VM
Values and the runtime library
Type ids
Every module starts with twelve built-in types. The types it declares are numbered from 12 on.
| Id | Type | Id | Type |
|---|---|---|---|
| 0 | error |
6 | string |
| 1 | void |
7 | any |
| 2 | null |
8 | [any] |
| 3 | int |
9 | map<string, any> |
| 4 | float |
10 | [string] |
| 5 | bool |
11 | [int] |
Runtime functions that have to understand a value take its static type id as an extra argument. For example
to_str takes the value and its type, so const 3; rt to_str formats an int and const 11; rt to_str formats
an [int] array.
Heap objects
Heap objects start with a 16-byte header: kind (1 byte), cycle collector color, flags, element kind, type id (4 bytes), size in bytes (4 bytes) and reference count (4 bytes). After the header:
| Kind | Layout |
|---|---|
| string | length at +16, UTF-8 bytes at +24 |
| array | length at +16, capacity at +24, pointer to the elements at +32 (8 bytes each, or packed, see below) |
| record | field i at +16 + 8·i |
| box | the boxed value at +16, with the type id of the value in the header |
| map, future | opaque, used through runtime functions |
The sized number types (int8, uint8, int16, uint16, int32, uint32, uint64, float32) are written
int8 ... float32 in bvm assembly. On the stack they are 64-bit words like every other value: integers are
sign- or zero-extended and float32 values are doubles rounded to single precision. Arrays whose element type is one
of the packed types (all of them except uint64) store each element in 1, 2 or 4 bytes; the element kind byte in the
header says which, and index, setindex and the array runtime functions convert between the packed bytes and
64-bit words.
Values of type any are boxes (or the object itself when it is already on the heap). rt box v tid creates one
and unbox reads it. rt is_type, rt cast and rt type_name inspect it.
Reference counting
Objects are reference counted. A new object has a count of 1, owned by whoever created it: the result of rt
functions, newarr, new and calls is owned by the code that receives it. rt retain v adds an owner and returns
v, rt release v removes one and frees the object when none is left, releasing what it holds. Both ignore null
and strings from the module's string pool, which are static.
Containers own their contents: newarr and new take ownership of the values they are built from, and rt
functions that store a value (arr_push, arr_set, map_set, box, ...) add their own owner, so the caller
still releases its copy. index, getfield and unbox read without adding an owner. Burn generates these
instructions itself; another language on bvm must do the same, or its objects are never freed.
Objects that only refer to each other are found by a cycle collector, which runs when enough memory has been
allocated (BURN_GC_THRESHOLD, 32 MB by default; BURN_GC_STATS=1 prints a line each time) and never while spawned
threads are running. While threads run, counts are updated atomically.
Runtime functions
rt name pops the function's arguments (the first argument is deepest on the stack) and pushes its result.
Functions without a meaningful result return 0, so pop it. bvm runtime prints every name with its argument
count. The most useful ones:
Written in Burn
The whole runtime is written in Burn, in lib/rt/ of the Burn repository: memory and values, reference counting and
the cycle collector, strings, arrays, maps, records, printing and number formatting, files, processes, the network,
JSON, time and tasks. It calls the C library through @Extern and works on raw memory
in unsafe code (std/memory). Each runtime function is a Burn function with a C name:
@Extern("burn_str_split")
pub unsafe fun strSplit(s: int, sep: int): int {
...
}The same code is compiled into the toolchain, where rt str_split calls burn_str_split directly, and into
libburn_runtime.a, which native executables link, so a program behaves the same on bvm and as an executable. A
runtime function raises a runtime error that points at loc, the place in the program that called it, as in
str_index, arr_get and fail below.
Text and output
| Function | Arguments | Result |
|---|---|---|
print, print_raw, print_err |
string | prints with a newline, without one, or to stderr |
to_str |
value, type id | the value formatted as text (arrays, maps and records included) |
str_concat |
a, b | a new string a + b |
str_eq, str_cmp |
a, b | 1 if equal / -1, 0 or 1 |
str_len |
s | length in characters |
str_index |
s, i, loc | the character at i as a string |
str_sub |
s, start, end | substring (character indexes) |
str_find, str_contains, str_starts, str_ends |
s, t | index or -1 / bool |
str_replace |
s, from, to | replaces every occurrence |
str_split |
s, sep | [string] |
str_trim, str_upper, str_lower, str_chars |
s | transformed string / [string] of characters |
str_repeat |
s, n | s repeated n times |
str_code, str_from_code |
s / n | code point of the first character / string from a code point |
char_class |
s, kind | 1 if every character is a letter (0), digit (1), letter or digit (2), whitespace (3) |
parse_int, parse_float |
s, loc | the parsed number; a runtime error if s isn't one |
is_int_str, is_float_str |
s | bool |
is_int_radix, parse_int_radix |
s, radix, loc | bool / the int in base radix |
int_to_str_radix |
n, radix, loc | n written in base radix |
input |
prompt | a line from stdin |
read_stdin |
all of stdin |
Arrays and maps
| Function | Arguments | Result |
|---|---|---|
arr_new |
array type id, length | a zero-filled array |
arr_push |
arr, v | appends |
arr_pop |
arr, loc | removes and returns the last element |
arr_insert, arr_remove |
arr, i, (v,) loc | inserts / removes and returns |
arr_get, arr_set |
arr, i, (v,) loc | bounds-checked access (the index instruction is faster) |
arr_slice |
arr, start, end | a new array |
arr_concat, arr_copy, arr_reverse, arr_clear |
arr (, other) | new array / in place |
arr_index_of, arr_contains |
arr, v, element type id | index or -1 / bool |
arr_join |
arr, separator, element type id | a string |
arr_sort |
arr, element type id | sorts in place |
map_new |
map type id | an empty map |
map_set, map_get, map_get_or |
map, key, value / loc / default | writes / reads |
map_has, map_remove, map_len |
map (, key) | bool / removes / count |
map_keys, map_values |
map, array type id | an array |
Everything else
| Function | Arguments | Result |
|---|---|---|
fmath |
op, x | 0 sqrt, 1 floor, 2 ceil, 3 round, 4 abs, 5 sin, 6 cos, 7 tan, 8 ln, 9 log10, 10 exp, 11 asin, 12 acos, 13 atan |
fpow, fatan2, fmod, fmin, fmax |
a, b | float math |
ipow, imin, imax, iabs |
a (, b) | integer math |
random, random_int, seed |
/ lo, hi / seed | float in [0, 1) / int in [lo, hi] / 0 |
now_ms, now_sec, clock_ns, sleep |
/ ms | wall clock and monotonic time |
read_file, write_file, append_file, file_exists |
path (, content or loc) | file I/O |
env, args |
name / | environment variable / [string] of program arguments |
json_parse, json_stringify |
text, loc / value, type id | any / string |
http_request |
method, url, body, [string] headers |
[status, body, headers] |
box, is_type, cast, unwrap, type_name |
value and type ids | dynamic typing helpers |
eq, cmp |
a, b, type id | structural equality / ordering |
await |
future | the result of a spawn |
panic, assert |
message, loc / cond, message, loc | runtime errors |
fail |
message, loc | a runtime error with message at loc |
exit_now |
code | waits for spawned threads, flushes output and exits |
gc_collect |
forces a collection |