Whale brings together an intermediate representation, assembler, object-file library, and linker infrastructure. Developed within the Wave ecosystem, its components are intended to serve language implementations and compiler tooling as reusable Rust libraries.
Whale is in active, early development. APIs and IR formats are evolving; the current capabilities are listed below.
Getting started · Usage · Development · Contributing · Support
- Defined behavior: specify program behavior explicitly, including invalid operations, with the goal of an IR without undefined behavior.
- Explicit O0 IR: keep operations and required safety behavior visible in IR, preserving unused operations and unreachable blocks for debugging. O0 is the current development priority; O1 and higher optimizations are future work. Verification diagnoses invalid IR without removing or simplifying it. Explicit constant declarations retain their typed initializer expressions and evaluated values, including unused local declarations. Their compile-time evaluation does not introduce runtime arithmetic instructions.
- Reusable components: expose the IR, assembler, object model, and linker as separate crates.
These are project goals. Complete memory-safety semantics and an end-to-end native compilation pipeline are still being developed.
| Component | Available today | Status |
|---|---|---|
| Assembler | AMD64 assembly, sections, symbols, and relocations emitted as ELF64 object files | Available |
| IR | Typed IR construction, printing, verification, and scalar AST JSON lowering | Experimental |
| Object library | Object model and ELF64 relocatable object serialization | Available |
| Object CLI | Wrap raw input bytes in an ELF64 object with a .text section |
Limited |
| Linker | Initial library infrastructure; whale link remains a placeholder |
In development |
The IR target selector accepts only x86_64-whale-linux; unknown targets fail
with the supported choice, including with --no-verify. Its output data layout
is 64-bit little endian on every build host. Library lowering and verification
reject target/layout mismatches. The IR layout API computes checked sizes,
field offsets, array strides, and natural/allocation alignments for this target.
It does not implement aggregate ABI passing or native code generation.
BSS reservations retain a logical zero_fill count instead of allocating their
zero bytes. Object section memory size is data.len() + zero_fill; non-BSS
sections require zero zero_fill. ELF output uses checked field conversions and
layout arithmetic and rejects extended section numbering. Its default output
budget is 256 MiB; library clients can override it with write_with_limit.
The linker layout API returns Result and records separate file/memory positions
and sizes for every input section. It does not yet emit executable segments.
The current emitted object target is AMD64 ELF64. Object metadata records the machine,
format, byte order, and address width; writers and linker inputs reject unsupported
combinations. ObjectFile::new(ObjectFormat::ELF64) remains an AMD64 convenience
constructor; explicit identities use ObjectFile::with_target, and format access
is now object.target.format. An object file is not a linked
executable. CI runs host checks on Linux, Windows, and macOS; running Whale on a
host does not imply support for that host's native object format or instruction
set as an output target.
You need Git and Rust 1.86.0 or newer, including Cargo. Stable Rust is recommended for development.
git clone https://github.com/wavefnd/Whale.git
cd Whale
cargo build --release --lockedThe executable is written to target/release/whale, or
target/release/whale.exe on Windows. The examples below use cargo run so that
installing Whale on your PATH is optional.
Enable the experimental IR command when building with:
cargo build --release --locked --features socket-cliSave the following as example.asm:
section .text
global answer
answer:
mov eax, 42
retcargo run --release --locked -- asm --amd64 example.asm -o example.oThis produces a relocatable ELF64 object. Assembly is implemented within Whale; no external assembler is needed.
Save this minimal typed AST as program.json:
{
"format_version": 2,
"semantics_version": 1,
"features": [],
"program": {
"globals": [],
"functions": [
{
"name": "answer",
"parameters": [],
"return_type": {
"Int": {
"bits": 32,
"signed": true
}
},
"body": [
{
"Return": {
"Lit": {
"Int": {
"bits": 32,
"signed": true,
"value": "42"
}
}
}
}
],
"convention": "Whale",
"linkage": "Internal",
"link_name": null
}
],
"declarations": []
}
}cargo run --release --locked --features socket-cli -- ir lower program.json -o program.wirThe command lowers and verifies the module, then writes textual IR to
program.wir. Omit -o program.wir to print it to standard output. The input
schema is documented in AST JSON Schema and
the frontend AST types.
The envelope requires format_version: 2, semantics_version: 1, and
features: []. Unversioned inputs, unknown fields/versions/features, duplicate
JSON keys, and trailing JSON are rejected even with --no-verify. The raw JSON
entry point is ir::lower_ast::interchange::decode; its default input limit is
8 MiB, configurable with decode_with_limit. Integers use decimal strings;
f16/f32/f64 values use 0x followed by exactly 4/8/16 hexadecimal storage digits.
For example, {"Float":{"bits":32,"value":"0x80000000"}} preserves negative zero.
Migrate format 1 inputs by setting format_version to 2, adding a declarations
array to program, and adding convention and linkage to every definition.
Internal functions use "Whale" and "Internal"; external functions must supply
a nonempty link_name. Missing versions and format 1 inputs are rejected.
Integers and floats retain their exact string encoding. AST and printed typed IR have independent format versions and a shared
semantics version; printed IR includes both version fields. Text parsing is still
unavailable.
The AST supports scalar literals, variables/constants, add/sub/mul, comparisons,
assignment, return, if/while, break/continue, function declarations, direct calls,
and typed function pointers with indirect calls. Aggregate expressions, variadic
signatures, and SysV64 aggregate signatures are not supported. Unsupported forms fail explicitly.
If the binary lacks socket-cli, whale ir exits with status 2 and prints the
feature-enabled recovery command shown above.
For a complete invalid input, save the following as invalid.json:
{
"format_version": 99,
"semantics_version": 1,
"features": [],
"program": {
"declarations": [],
"globals": [],
"functions": []
}
}cargo run --locked --features socket-cli -- ir lower invalid.json -o rejected.wirThis exits nonzero with unsupported AST format_version 99; expected 2. It does
not create rejected.wir; an existing output is preserved. Lowering/type errors
likewise fail before output publication.
The complete call input lowers a local function, a stored function pointer, an indirect call, and a SysV64 external call. Its printed IR is checked by a regression test.
cargo run --locked --features socket-cli -- ir lower ir/tests/fixtures/ast-v2-calls.jsonDeclarations carry FunctionId, parameter/result types, calling convention,
linkage, and explicit external link names. Calls resolve by function identity or
by a fnptr<signature> SSA value. The verifier checks arity, argument/result
types, conventions, and indirect-callee dominance. Callee expressions evaluate
before arguments, which evaluate left to right. Void calls have no result;
unused nonvoid results remain in O0 IR. Null or invalid indirect targets have a
defined trap contract; runtime checks await the interpreter/native backend.
This IR support does not implement machine ABI lowering or carry identities
through object emission and linking yet.
For an existing raw binary file:
cargo run --release --locked -- object code.bin -o code.oThis places the input bytes in an ELF64 .text section and defines a global
start symbol at offset zero. It does not compile textual IR or disassemble
existing object files.
The default Rust path needs no Wave compiler. An explicit build can use Wave for ELF64 header, section, symbol and RELA record serialization. Layout, validation, allocation and symbol resolution remain in Rust. This bootstrap supports a Linux x86_64 host building a Linux x86_64 Whale binary; it does not add an output target.
With Rust, LLVM 21 development libraries, a C linker and ar installed:
git clone https://github.com/wavefnd/Wave.git /tmp/whale-wave-bootstrap
git -C /tmp/whale-wave-bootstrap checkout --detach 8a465e30aeea4b817d925cdd0e8d08c1bb029c9a
python3 tools/build_wave_elf.py --wave-source /tmp/whale-wave-bootstrap --out-dir /tmp/whale-wave-elf
WHALE_WAVE_ELF_DIR=/tmp/whale-wave-elf cargo build --locked --all-features
WHALE_WAVE_ELF_DIR=/tmp/whale-wave-elf cargo test --locked --workspace --all-featuresThe script verifies the source revision and tracked modifications, builds the
bootstrap compiler with its lockfile, then compiles object/wave/elf_records.wave
with its LLVM backend. The Wave object is linked statically; the resulting Whale
needs no wavec at runtime. An invalid requested archive/host is a build error,
not an automatic fallback. Unset WHALE_WAVE_ELF_DIR to build the Rust path.
object::formats::elf::WAVE_ELF_ENABLED reports the selected build path.
The ABI uses u64 field arrays and caller-owned output buffers with explicit counts/capacity. No allocator ownership crosses the boundary. The Wave routine rejects unsupported records, short buffers and field overflow before writing. Dedicated tests compare complete ELF output with the Rust path. This is the first partial Wave implementation, not a self-hosted Whale build.
| Path | Responsibility |
|---|---|
| assembler/ | Assembly parsing and instruction encoding |
| ir/ | IR types, builders, lowering, verification, and printing |
| object/ | Sections, symbols, relocations, and ELF serialization |
| linker/ | Symbol resolution and layout infrastructure |
| src/ | Command-line interface |
| tests/ | CLI integration tests |
| tools/ | CI helpers and standalone CLI smoke checks |
Run the workspace checks from the repository root:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
cargo test --workspace --locked
cargo test --workspace --all-features --lockedGitHub Actions also covers native host configurations, the minimum supported Rust version, optimized tests, rustdoc, coverage, and scheduled maintenance. The checked-in workflows contain the commands used by CI.
Contributions to correctness, diagnostics, tests, and toolchain capabilities are welcome. Read CONTRIBUTING.md for setup, validation, and signed-off commits.
- Find a bounded task in good first issues.
- Follow planned work in the toolchain backlog.
- Report a bug or propose a feature.
- See MAINTAINERS for review contacts and the Code of Conduct for community expectations.
The repository's AI-use policy is recorded in ai.txt.
Support development through Open Collective or GitHub Sponsors.
Whale is licensed under the Mozilla Public License 2.0. See COPYRIGHT and NOTICE for attribution and notices.