Repository navigation
Torvik v1.5.0 — "The Forge"
Systems and OS development.
Torvik can now produce a freestanding image that boots on bare hardware — no operating
system, no C library, and no runtime beneath it. The reference kernel in this release writes
to VGA text memory, drives hardware I/O ports, and parks the CPU, and every line of it is
Torvik apart from a sixty-line boot stub.
Everything new here is opt-in and unsafe-gated. Torvik has no unsafe mode; it has
unsafe statements, and you write the word each time. If you never write it, nothing in this
release can reach an address it shouldn't — buf[i] in the function next door is still
bounds-checked, and reference counting still cleans up after your strings.
The Forge
Raw pointers — varda<T>
from_addr, as_addr, ptr_add (element-scaled), ptr_byte_offset (raw bytes), load,
store. Plus addr_of, null_addr and is_null, which are safe — they work with something
you already own rather than conjuring an address from a number.
Fixed arrays — [T; N]
Inline storage, array_zero(), array literals with a checked element count, and
bounds-checked indexing that catches negatives too. The safe surface stays safe.
Value structs — shape
Fields read and write, take part in arithmetic and comparison, and addr_of(p.field) gives a
typed pointer into one. Omitted fields are zero, not stack garbage. A packed shape maps
byte-for-byte onto a C struct or a hardware descriptor:
packed shape GdtEntry {
limit_low: u16, base_low: u16, base_mid: u8,
access: u8, flags: u8, base_high: u8
}
// size_of(GdtEntry) == 8, exactly as the manual says
size_of(T) / align_of(T)
Compile-time layout facts for any scalar or shape. Layout comes from LLVM, so packed
reports what the hardware expects rather than what a table someone maintains says.
Volatile memory — load_vol / store_vol
Never elided, merged or reordered. A store to a hardware register that nothing reads back is
dead code to an optimiser; volatile is what makes the write actually happen.
Inline assembly — galdr
unsafe galdr { "cli"; "hlt"; }
One instruction per string, emitted so the optimiser leaves it exactly where you put it.
Common operations have named intrinsics instead — cli, sti, hlt, pause_cpu,
outb/outw/outl, inb/inw/inl — so the register constraints live in the compiler,
written once and tested, rather than in every kernel.
Float widths — f16, f32, f128
Alongside f64, with true storage layout: shape Vec3 { x: f32, y: f32, z: f32 } is twelve
bytes, which is what a GPU buffer or a C struct expects.
Hex, binary and underscored integer literals — 0xB8000, 0b1010_0110, 1_000_000.
Freestanding builds
torvc kernel.tv --bare -o kernel.elfNo libc, no runtime unless you ask for one. --no-std, --entry, --arch,
--link-script and --link-with come with it.
The allocator hook. Heap types need memory, and bare there is no malloc. Define
on_alloc / on_free and str, list and the rest work in a freestanding image; leave them
out and the runtime isn't linked at all.
OS builtins are refused at compile time, with an error that says what to use instead —
rather than an undefined-symbol dump from the linker after you've already got it wrong.
The output is decided by the target, not the host. A kernel built on Windows is the same
ELF as one built on Linux.
examples/kernel/ ships a complete reference kernel: VGA text output, port I/O for the
hardware cursor, an allocator, a long-mode boot stub, a linker script, and a
Forge your first kernel tutorial.
Tooling
Run mode. torvc file.tv compiles and runs a file, leaving no binary
behind. Arguments after the filename reach the program through args_get:
torvc scan.tv report.csv data.csvrune run file.tv [args...] does the same; a bare rune run still runs the project.
The [build] manifest section. Declare target = bare, an entry symbol, an
architecture, a link script, a boot stub and a runner once, and rune build / rune run
build and launch a kernel. A bare project builds to .elf, and rune run hands it to the
runner rather than trying to execute something the host can't.
Major-version gating. rune update now keeps you on your current major and tells you
about a new one instead of installing it — while still pulling the newest release within the
major you're on. Opt in with rune update v2 --yes. --silence-major quiets the notice
until the next major ships, and refuses if there's nothing to silence.
Changed
&& and || now short-circuit. They previously evaluated both operands, which meant a
guard like i < len(xs) && xs[i] == 1 could read out of bounds. This is a correctness fix.
Builtin results fold into surrounding expressions. byte_at(s, 0) * 2 and args() >= 2
used to fail with a confusing three-error cascade; only a handful of builtins chained
correctly. All of them do now.
The standard library moved to its own repository
with its own version line, so a project can hold std steady while the toolchain moves on.
Pin it with std = "1.3.0" in your manifest; rune update --std-major --yes opts into a new
major and writes the pin for you. Installing an older Torvik still works exactly as before.
rune is tested in its own repository, so each project can be checked on its own.
Fixed
- A user identifier named
entrymade the compiler fail with an internal error — LLVM shares
one namespace for values and block labels, and every function emitted anentry:label. - Defining a function or global named after a linked symbol (
puts,malloc,torvik_argc)
reported an internal error asking you to file a compiler bug, for what was a naming choice. - A decimal used as an index, and several float paths, produced internal errors.
- Reporting a diagnostic could itself crash the compiler on an out-of-range position.
- Paths containing spaces broke the build.
galdrassembly text is escaped, so a quote inside an instruction can no longer inject
arbitrary IR.- Shape and array variables were wrongly reported as unused when only their fields or
elements were read. - Unsupported field and element types (
str,list,i128, …) used to compile and store
garbage; they are now refused with an error listing what is allowed.
Notes
f128is a storage and interop type. Arithmetic on it computes at double precision —
declaration, storage and layout are exact, computation is not. x86 also has no hardware for
128-bit floats, so real f128 arithmetic needs soft-float support that a--barebuild does
not link.- Heap-backed types (
str,list,table,bag,i128/u128), nested shapes, and shape
parameters are not supported in shapes or fixed arrays yet. They need reference counting
threaded through struct copies, which is real work and is scheduled for a future version. Unsupported types are refused with a clear error, never mis-stored.
Install
# Linux
curl -fsSL https://raw.githubusercontent.com/torvik-lang/torvik/main/linux/install.sh | sh
# Windows (PowerShell)
iwr -useb https://raw.githubusercontent.com/torvik-lang/torvik/main/windows/install.ps1 | iexAlready installed? rune update.
Docs: Language guide ·
Build a kernel ·
Tooling