Skip to content

Releases: rynlanguage/Ryn

Ryn 0.1.13: registers, AArch64, variadic C calls

Choose a tag to compare

@v3nn7 v3nn7 released this 12 Oct 18:08

Native language fixes (2026-10-13): the Windows ZIP and its SHA-256 were rebuilt from commit d7ef69b. Custom-destructor resources can be moved into enum payloads, propagated with ? and extracted with unwrap/expect/unwrap_err without duplicate handles or destruction. Integer as char validates Unicode scalars without truncation. Map() infers key/value types from annotations and structure fields. use module::PublicItem imports public types/functions/aliases. Checking a source file discovers its enclosing manifest and resolves path dependencies relative to it; Windows CLI path separators are normalized. Includes all previous keyboard/thread/mutex and FFI fixes. Verified on Windows: 26 tests, including six existing ownership programs and packaged check/build/run, and byte-identical compiler bootstrap stages. The Linux assets and release tag are unchanged.

Windows threads and mutexes (2026-10-13): the Windows ZIP and its SHA-256 were rebuilt from commit f0bb305. std::thread::Thread now supports spawn/id/join/drop; std::sync::Mutex, MutexGuard and lock_raw use exclusive non-recursive SRW locks. The Windows allocator serializes parallel malloc/free. Includes the keyboard hotfix and all previous FFI fixes. std::sync::JoinHandle, RwLock, Condvar, Barrier, Once and Local remain Linux-only. Verified on Windows: eleven regression tests including packaged check/build/run, and byte-identical compiler bootstrap stages. The Linux assets and release tag are unchanged.

Windows keyboard hotfix (2026-10-13): the Windows ZIP and its SHA-256 were rebuilt from commit c1f72f6. std::keyboard now supports continuous key state, pressed/released edges and read_key through user32.dll, fixing R0905 (opendir). Includes all previous FFI fixes. Verified on Windows: five regression tests including packaged check/build/run, and byte-identical compiler bootstrap stages. The Linux assets and release tag are unchanged.

Updated: this release now also includes pointers to array elements and C strings for FFI: &array[index] of a local integer or bool array passed to an extern "C" function, and std::cstring::CString for str as a NUL-terminated C string. Also includes the earlier fixes (unknown type and enum diagnostics, and passing &local to pointer parameters). The archives were rebuilt from commit e8321af.

Updated: this release now includes the fixes for unknown types and enums (R0230 / R0233 with the name and location) and for passing a local by reference to a pointer parameter of an extern "C" function (for example time(&value), also through a function pointer from load_symbol). The archives were rebuilt from commit de03ba2.

Updated: this release now includes the fix for unknown types and enums (R0230 / R0233 with the name and location instead of the generic unsupported error). The archives were rebuilt from commit cfccd66.

Released together with the work of 0.1.11 and 0.1.12, which had no separate release.

  • extern "C" fun printf(format: *u8, ...) -> i32;: ... after the named parameters of an extern "C" function. Call
    it with any number of variable arguments: integers of any width, f64 and raw pointers are passed as they are; an
    f32 (R0270, write as f64), a string or a structure is rejected. The native backend sets al to the number of
    vector registers in use before each C call, as the System V convention requires. Variadic calls on Windows are
    rejected. ... in a non-extern "C" function is R0010. Tests: tests/suite/ffi/variadic_*.
  • The function-record encoding carries a variadic flag (parser, AST and generics).
  • Documentation: docs/language-0.2.md replaces the old "cannot be declared" limitation.
  • x86-64 encoder (selfhost/src/back/x64.ryn): EVEX for 512-bit zmm0-zmm31 (evex_rr, zmm_move_at, opmask
    {%kN} and zeroing {z}), opmask instructions (kmovw/kmovq to and from general registers, kandw, korw,
    kxorw, knotw, kortestw, ktestw), MMX on mm0-mm7 (mmx_rr, movq, emms), and VEX registers 8 to 15 for
    ymm (the VEX prefix now carries R and B). tools/tests/test_encoder_bytes.py checks 31 instructions against llvm-mc
    (19 identical bytes, the rest equivalent forms that decode the same). These encodings are not yet reachable from Ryn
    programs; the AVX-512 forms have not been executed, since the development machine has no AVX-512.
  • Vector types from Ryn programs: std::simd has 512-bit types F32x16, F64x8, I32x16, I64x8 (generated by
    tools/gen-simd.py). Arithmetic uses EVEX on one zmm register when std::cpu reports AVX-512F and two 256-bit halves
    otherwise. Comparisons return integer lane masks (u16 for 16 lanes, u8 for 8); blend(other, mask) merges by mask.
    The AVX-512 form is verified by its bytes and by llvm-objdump only: this host has no AVX-512.
  • std::mmx (Mmx, a 64-bit value): add, subtract, multiply, compare and bit operations on 8, 16, 32 or 64-bit lanes.
    Each operation runs in mm0/mm1 and ends with emms. Tests: tests/suite/mmx/, tests/suite/simd/zmm_*.
  • AArch64 backend for Linux: ryn build --target aarch64-linux and rync --target aarch64-linux write a static ARM64
    executable from the same IR. The subset (integer and bool functions, loops, echo of integers and bools, self tail
    calls, the x86-64 runtime errors and their exit statuses) is compiled; everything else is refused with a named reason.
    Verified by encoding checks against llvm-mc and by running the suite's accepted programs under the Unicorn emulator:
    70 of 538 run programs are accepted, all match. See docs/arm64-backend.md.
  • RYN_DUMP_IR=<path> writes the IR text of a program (a debugging aid for the code generators).

Full changelog: https://github.com/rynlanguage/Ryn/blob/v0.1.13/CHANGELOG.md

Ryn 0.1.10

Choose a tag to compare

@v3nn7 v3nn7 released this 12 Oct 13:57

Code speed: a fourth pass over what rync generates for calls, locals, small functions and frames, with the assembly of
the same programs in Rust and C++ next to it (docs/native-performance.md, benches/versus/). RYN_NO_OPT=1 turns it all off.

  • Arguments of a function with at most six one-cell parameters travel in registers (rdi, rsi, rdx, rcx, r8, r9),
    not on the stack; the callee does not copy them. fib(40): 49 instructions and 219 bytes became 25 and 104.
  • The four most used scalar locals of a function live in r12 to r15 (saved in the frame, restored on every return);
    x = x + c, comparisons and lea use them directly.
  • A function with no owned value no longer zeroes its frame (14 instructions per call); leave, no second epilogue, no jump
    over an empty else; a comparison is one compare and one jump (&&, ||, ! are jumps); x & mask == 0 and
    x % 2^k == 0 are one test; loops test at the bottom; when c { x = a } else { x = b } of integers is a cmov.
  • Small functions of let, assignment, if and return (four parameters, eight locals, 40 nodes) are inlined, with their
    locals in the caller's frame.
  • Signed division and remainder by any constant use the multiply-high form (only unsigned and powers of two had it);
    indexing and set of a vector with a constant or local index push nothing.
  • Measured against rustc -O and g++ -O2 on one machine: fib(40) 1.07 s to 0.67 s (Rust 0.32, C++ 0.16), collatz
    1.05 s to 0.58 s (0.44, 0.32), the sieve 0.45 s to 0.39 s (0.12, 0.10); the hash map is unchanged at 0.76 s (0.25, 0.27).
    Ryn starts in 0.19 ms (Rust 0.62, C++ 0.43), hello is 8 KB (4.3 MB, 16 KB), 3000 functions build in 2.5 s (15.2 s, 13.3 s),
    std::simd::sum_f32 sums in 0.0085 s (0.052 s, 0.014 s) and 5 000 000 list nodes from std::alloc::Pool take 0.15 s
    (0.21 s, 0.21 s). Ryn is slower on compute-bound code, as the document says.
  • Standard output that is not a terminal is buffered on Linux (64 KiB, flushed at exit, after a panic or runtime error, before
    an error message, before reading standard input or starting a process, and by io::stdout_flush()); a terminal and a program
    with threads write at once. Printing 1 000 000 lines: 1.50 s to 0.037 s (Rust 0.46 s, C++ 0.068 s). The frame cell of a local
    that lives in a register is no longer written (checked by running the suite and the bootstrap with RYN_REGPOISON=1).
  • Seven new tests in tests/suite/ (optimize/ and output/); suite 695/695, tools/run-tests.py 260 tests. A function with only a few
    statements is now inlined, so tools/tests/test_perf_019.py gives its functions a loop to keep them out of line.

Ryn 0.1.9

Choose a tag to compare

@v3nn7 v3nn7 released this 12 Oct 11:45

Compiler speed, code speed and systems features. docs/native-performance.md has the state of each item, the
measurements and the limits. Suite 688/688 with the self-built compiler. The committed seed is still the 0.1.8 compiler:
stage2 == stage3 holds when the seed is a compiler built from this source (one stage), and the seed in bootstrap/ has to be
updated to a stage2 of this tree (and its checksum regenerated) before the CI bootstrap, which starts from the seed, passes.

Scalar optimizations (RYN_NO_OPT=1 turns them off): constant propagation of locals defined once, removal of
unreachable statements, constant if conditions and unused scalar lets, strength reduction (division and remainder
by constants without a divide, signed shifts, immediates), self-recursive tail calls in constant stack, and operands
read without push/pop. loop_sum 1.97 s to 0.49 s. Copy propagation, loop invariant code motion, common
subexpression elimination, loop bounds check elimination and escape analysis are not implemented
(docs/native-performance.md says why). Suite 687/687.

Compiling the compiler: 15.5 s to 6.7 s, bootstrap 32 s to 23 s, same peak memory. A field of a vector element
(self.structs[id].field_names) is read where the element lies and cloned alone, or lent to a builtin that only
borrows it, instead of copying the whole element; a few linear scans became hash lookups.
RYN_SYMBOLS=1 writes the function offsets of a build to standard error, for profiling the compiler.

Compiler speed (all measured on one machine, medians of 5 runs; "before" is the shipped 0.1.8 bootstrap/rync-seed):

  • Lowering finds types, structures, functions and aliases through hash indexes instead of scanning every declaration
    (type_index, struct_index, signature_map, alias_index), and the code generator finds drop and glue routines
    the same way. This was the quadratic part: compiling the compiler took 152 s and takes 15 s; the bootstrap
    (stage2 + stage3) took 4 min 50 s and takes 32 s.
  • Functions of the native runtime module (std::native) are lowered only when something reaches them, so a small
    program no longer pays for the whole module: hello compiles in 0.11 s instead of 0.38 s. RYN_LOWER_ALL=1
    lowers all of them (to check the module itself).
  • RYN_TIMINGS=1 makes rync write the time of each phase to standard error.

Code speed and size:

  • Leaf functions whose body is one small expression are inlined; a structure literal that is returned is written
    straight into the result cells; expressions made of literals are folded to immediates (and multiplications,
    divisions and remainders by a power of two become shifts and masks); a small frame is zeroed by a counted qword loop.
    inline_leaf_calls: 1.11 s to 0.15 s; alloc_pool: 0.58 s to 0.22 s.
  • Only the runtime routines the code names are written, equal string literals share one copy, and large blocks own their
    mapping: a hello program is 8.3 KB instead of 16 KB.
  • Profile-guided code layout: rync build --profile-generate FILE makes an executable that writes the entry count of
    each function at exit; rync build --profile-use FILE generates the functions most entered first. An unreadable
    or damaged profile only costs the layout (a warning is printed).
  • std::simd::sum_f32, sum_i32, dot_f32 and add_f32: fused array loops that keep the vector registers across
    iterations (AVX2 when std::cpu reports it, SSE2 otherwise, identical results on both). simd_fused_sum: 0.010 s
    against 0.27 s for the scalar loop.

Systems features (see docs/native-performance.md for the exact state and the limits).

  • std::bits, std::mem, std::overflow: bit and memory intrinsics (compiled to bsr/bsf/bswap/rol/ror/rep movsb), checked, wrapping and saturating arithmetic.
  • std::atomic (Atomic<T>, AtomicCell, orderings; lock xadd, lock cmpxchg, xchg, mfence), std::sync (threads with arguments, Mutex, RwLock, Condvar, Barrier, Once, thread-local storage).
  • std::alloc: Stack and Pool allocators next to std::arena.
  • #[repr(C, packed)], #[repr(C, packed(N))], #[repr(C, align(N))]; compile-time size_of::<T>(), align_of::<T>(), offset_of::<S>("field").
  • std::cpu (CPUID and XGETBV with OS-state checks) and std::simd (SSE2/AVX2 vectors with runtime dispatch and scalar fallback).
  • ryn asm (assembly listing, --function, --summary, --compare), ryn update, and benches/ for ryn bench.
  • Fixes: operator methods (a + b) on structures declared in a module; instances of a public generic structure are public; IR carries function names.
  • Known: the SIMD vector types are still slower than the scalar loop (only the fused array loops are fast); no ARM64/NEON.

Ryn 0.1.8

Choose a tag to compare

@v3nn7 v3nn7 released this 11 Oct 19:18

Maintenance release. The version strings say 0.1.8; the compiler and the language are unchanged from 0.1.7.

  • Issue and pull request templates and SUPPORT.md no longer ask for a cranelift backend or ryn bootstrap --native
    (both were removed in 0.1.6); they ask for the target and point at tools/bootstrap-native.sh.
  • docs/native-ownership-validation.md, docs/native-float-validation.md and docs/language-0.2.md are marked as
    historical: they describe the Rust and Cargo era.

Ryn 0.1.7 — Native Power & Benchmarks

Choose a tag to compare

@v3nn7 v3nn7 released this 11 Oct 13:00

"Native Power & Benchmarks". The version strings (VERSION, ryn --version, rync --version, the registry header, the CI
archive names) say 0.1.7.

Nested structures

  • A path of fields is stored through a reference: self.q.p.y = 1 and l.a.x += 1 in a mut self method or through a
    &mut parameter (the native backend declined these as "assignment through this reference"). The store goes to the
    address of the nested field (PlaceAddress in the IR), so the old value is dropped and the new one moved in once.
  • A mut self method is called on a structure nested in another one (self.b.shift(1), l.b.shift(1)), in place, with the
    mutability of the root checked (R0204 on an immutable local). It was R0235 ("method receivers must be a local
    variable").
  • A field of a structure nested in the result of a call is read (make().q.p.x), and the rest of the result is dropped.
  • A method is called on a String field of a call result (make().label.len(), make().q.p.name.clone()): the string moves
    into a hidden local that lives as long as the enclosing block, so nothing leaks or is freed twice (it was R0905). A let
    whose statement needed such a temporary no longer drops the bound name at the end of the statement.
  • A structure stored through a pointer as a whole (self.seen = ... of a Vec, a String or a nested structure) works
    for the native backend; it was declined as "a structure field of a structure type". This is what stopped programs with
    a collection field changed through self inside loops from compiling.
  • C calls with more than six integer arguments pass the rest on the stack, for named functions and for calls through a
    function pointer (scalar arguments only; records and the freestanding and Windows targets still decline).
  • The help of R0905 no longer points at the removed --backend cranelift.
  • Tests: tests/suite/structs/nested_struct_*, vec_field_mutated_through_self_in_loops,
    raii/nested_destructor_runs_once_per_resource, ffi/foreign_call_with_stack_arguments.

ryn bench

  • ryn bench <file.ryn|project-dir> compiles the program, times the compiler (--compile-runs N), runs the program
    --warmup N times without counting and then --iterations N times with its output captured, and prints the minimum,
    median, mean, maximum and standard deviation. --json prints the report as JSON, --save-baseline FILE writes it,
    --baseline FILE --threshold PERCENT compares medians (default 10%) and exits 1 on a regression. A project with a
    benches/ directory times every .ryn file in it by name. Written in Ryn (selfhost/src/driver/bench.ryn).
  • The peak resident memory of the compiler and of the program is reported and written to the JSON (peak_memory_kb), and a
    baseline that has it makes memory growth past the threshold a regression too. std::process::ProcessResult has a new
    field peak_memory_kb (the child's ru_maxrss from wait4; 0 on Windows, where it is not measured).
  • Tests: tools/tests/test_bench.py.

Ryn 0.1.6

Choose a tag to compare

@v3nn7 v3nn7 released this 10 Oct 23:38

Ryn 0.1.6 "Native First".

The toolchain is written in Ryn. The ryn command, the native compiler rync (lexer, parser, analysis, code generator, ELF and PE writers) and the runtime are Ryn code; the Rust implementation and the Cranelift backend are removed. The compiler is built from the committed seed (bootstrap/rync-seed) by tools/bootstrap-native.sh, which requires stage 2 and stage 3 to be byte-identical (they also equal the seed).

Archives (each is one directory; ryn finds rync and stdlib next to itself):

  • ryn-0.1.6-linux-x86_64.tar.gz: ryn, rync, standard library, compiler sources, bootstrap script.
  • ryn-0.1.6-windows-x86_64.zip: ryn.exe, rync.exe, standard library (cross-compiled by the Linux compiler).

What was verified (from a clean clone, PATH=/usr/bin:/bin, no cargo/rustc/cc/ld): bootstrap fixpoint; 639 of 641 suite programs (2 have no test header); 241 Python tests; the archives run.

Limits, stated plainly:

  • Windows is experimental and was run under Wine only, never on Windows itself. Threads, sockets, pipe/fork, keyboard input and records passed to or returned from C by value are not available there (declined when compiling).
  • The GitHub workflows (CI and release) were rewritten for the Cargo-free build and have not been run before this release.
  • Linux x86-64 is the only supported native host; macOS is not supported.
  • Exact spans and help texts of the removed Rust analyzer are no longer pinned by tests (codes and messages are).

See CHANGELOG.md for the full list; the Rust implementation remains in the git history before commit 950e152.

Ryn 0.1.5

Choose a tag to compare

@v3nn7 v3nn7 released this 09 Oct 22:02
21f9a8b

Ryn 0.1.5 brings deterministic ownership to the native backend and a verified native compiler bootstrap. No garbage collector is introduced.

Native compiler and ownership

  • Deterministically release String, Vec, Map, Option, Result and owning enum payloads, including temporaries, replacements, loop exits and early propagation.
  • Clone nested enum payloads and unborrowed reference fields independently; invalidate moved owners to prevent repeated frees.
  • Verify native seed → stage2 → stage3 bootstrap under a 1 GiB address limit, with byte-identical stage2 and stage3 binaries.
  • Add live allocation/byte accounting and regressions for double-free, nested ownership and 30 million short-lived allocations.

Compiler and language improvements

  • Shared Ryn frontend and middle end, with Rust analyzer parity checks.
  • Updated output functions, compile-time constants/evaluation, metadata and derive support, nested patterns, generic extensions, C ABI handling and std::arena.
  • 571-program language regression corpus, plus native ownership and compiler project parity tests.

Freestanding float text and math

  • The native backend embeds the typed IR of the Ryn float runtime, so shortest round-tripping formatting and direct-width parsing (binary32 and binary64) need no libc: f32 parsing no longer rounds through f64.
  • std::math runs without libm on SSE2 and x87: sqrt, rounding, logarithms, trigonometry with fprem1 reduction, and pow with full special-case classification.
  • The 17 known freestanding float-text declines of earlier notes are resolved: the native sweep is 424 passing cases, 0 declined.

Downloads

Linux x86_64 and Windows x86_64 archives are accompanied by SHA-256 checksums, plus a Linux rync-0.1.5-linux-x86_64 archive with the self-hosted compiler, its Ryn sources, the standard library and the native bootstrap script. The standard ryn executable uses the Rust/Cranelift backend.

Validation and limits

  • Full Linux Cargo validation: 382 passing tests with a 16 MiB test-thread stack.
  • Native sweep: 424 passing cases, 0 declined; the 17 former float declines are fixed.
  • Native bootstrap peak RSS: 403528 KiB; the native stage2/stage3 fixpoint is byte-identical.
  • Native/Rust stress program outputs match. The Rust-built full compiler comparison exceeded the same 1 GiB limit, so no completed Rust/native full-compiler binary comparison is claimed.
  • rync's own frontend pipeline still passes 303 of 424 corpus programs directly from source; the remaining cases are compile-time evaluation, compile-time function calls, metaprogramming, some pattern forms, module path lookup and C imports.

Exact measurements and reproduction commands: https://github.com/rynlanguage/Ryn/blob/v0.1.5/docs/native-ownership-validation.md and https://github.com/rynlanguage/Ryn/blob/v0.1.5/docs/native-float-validation.md

Ryn 0.1.2

Choose a tag to compare

@v3nn7 v3nn7 released this 07 Oct 17:54

Ryn 0.1.2 adds packages from the pods registry.

  • Version dependencies such as rynix: 0.2.0 resolve from https://pods.ryn-lang.xyz with Cargo-style requirements (^, ~, =, *).
  • ryn lock pins each package version and the SHA-256 of its archive; the first build without ryn.lock creates it. Downloads are verified before they are unpacked.
  • ryn login <token> and ryn publish [project-dir] upload packages.
  • ryn.yaml accepts description, license, repository, homepage, and keywords.

The Windows x86_64 archive is attached; the Linux archive is added by the release workflow.

Ryn 0.1.1

Choose a tag to compare

@v3nn7 v3nn7 released this 07 Oct 17:26

Ryn 0.1.1 is a correctness update.

  • Copyable method receivers used in a when / else when condition, a for range bound, or a for ... in collection are evaluated before the condition. Previously the temporary was emitted into the first statement of the branch or loop body, so a condition such as when first(V::ZERO.add(V::ONE)) != 1 could read an uninitialized value and take the wrong branch.
  • Raw pointers and function pointers can be compared with == and !=.
  • An integer literal range start takes the end bound's type, so for i in 0..items.count() type-checks when count() returns u64.

The Windows x86_64 archive is attached; the Linux archive is added by the release workflow.

Ryn 0.1.0

Choose a tag to compare

@v3nn7 v3nn7 released this 07 Oct 16:17

Ryn 0.1.0 introduces a self-hosted compiler frontend. The Ryn lexer, parser, and generic specialization frontend are used by check/build/run; bootstrap verifies a three-stage fixpoint. Semantic analysis, Ryn Guard, Cranelift code generation, and the CLI remain Rust components.