Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 25. From Source to Bytecode

Goal

By the end of this chapter you will understand what keleusma run has been doing all along, and you will be able to compile a program into a file that runs directly.

Source and bytecode

A .kel file holds source: the text a person writes and reads. The virtual machine does not run source. It runs bytecode, a compact form produced from the source by the compiler. Every time keleusma run has been used in this guide, it has quietly done four steps in a row: read the source, compile it to bytecode, verify the bytecode, and run it.

Those steps can be separated. The compiling can be done once, ahead of time, and the result saved.

Compiling ahead of time

Write a small program and save it as tune.kel:

fn main() -> Word { 60 + 7 }

Compile it:

keleusma compile tune.kel -o tune.kel.bin

The tool prints:

wrote tune.kel.bin (2400 bytes)

tune.kel.bin is the compiled bytecode. Run it directly:

keleusma run tune.kel.bin

The output is 67. The tool recognized the file as bytecode and ran it without compiling, because the compiling was already done.

What is in a bytecode file

A bytecode file is a self-describing package. It begins with a short marker so the runtime can recognize it as Keleusma bytecode. After the marker comes a header carrying the program’s facts, then the program body, and at the end a checksum. The runtime reads the header before anything else and refuses a file that is not genuine Keleusma bytecode or that is built for an incompatible machine. The checksum lets it detect a file that was damaged in storage or transit.

A beginner does not need the byte-by-byte layout. The point is that a bytecode file is checked, by the runtime, before a single instruction of it runs.

A compiled file can carry a shebang

Chapter 2 added a shebang line to a source file on macOS and Linux. A compiled bytecode file can carry one too, so a finished, compiled program can be made directly runnable in the same way.

Why compile ahead

Compiling once and shipping the bytecode has two benefits. The machine that runs the program does not need the compiler, only the runtime. And the program starts at once, with no compile step first. Bytecode is the finished, engraved score, ready to hand to a player, as distinct from the working manuscript that the source is.

Selecting a target

The default compile targets the same machine running the compiler. To build a bytecode artefact for a different machine, pass --target:

keleusma compile tune.kel --target embedded_16 -o tune.kel.bin

The recognised target names are host (the default), wasm32, embedded_32, embedded_16, and embedded_8. The chosen target controls word, address, and float widths and validates the program against the configuration. A program that uses literals or constants outside the target’s representable range is rejected at compile time.

What you now know

  • Source is the text you write; bytecode is the compact form the runtime executes.
  • keleusma run compiles and runs in one step; keleusma compile produces a bytecode file you can run later.
  • A bytecode file is self-describing, version-checked, and protected by a checksum.
  • Compiling ahead of time means the running machine needs only the runtime, and the program starts immediately.

If you want to see the individual instructions the runtime executes, the Instruction Set reference lists every opcode with its operands, cost, and effect on the time and memory budgets. The playground shows the same disassembly for any program you write.

The next chapter covers two more things that happen to a finished program: it can be signed, and it can be swapped.