Files
zig/stage0
Motiejus 09df9ce993 stage0: pass store_to_pointer.zig (num_passing=31)
Fix three issues in zirFunc pre-creation to match Zig's funcCommon:

1. Resolve param types before func_type creation: iterate param_block
   and call resolveZirTypeRef/resolveZirTypeInst for each param's type
   body, creating IP entries (e.g. *u32) before the func_type entry.
   This matches the Zig compiler's funcCommon which evaluates param
   types before creating the func_type.

2. Use ipForceIntern for func_type: the C sema's func_type key only
   includes param_count (not actual param types), causing incorrect
   deduplication between functions with different param types but the
   same count. The upstream Zig IP includes param types, so different
   functions always get distinct func_types. ipForceIntern avoids the
   false dedup.

3. Fix sentinel=IP_INDEX_NONE in all ptr_type creation sites: 6 sites
   were using memset(0) which gives sentinel=0 instead of IP_INDEX_NONE
   (UINT32_MAX), preventing deduplication with correctly-created pointer
   types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 00:08:27 +00:00
..
2026-02-20 12:33:48 +02:00
2026-02-26 00:45:27 +02:00
2026-02-17 10:56:11 +00:00

About

zig0 aspires to be an interpreter of zig 0.15.2 written in C.

Except for the lexer (written by hand by yours truly), it's been written by an LLM.

The goal of stage0 is to be able to implement enough zig to be able to build zig1.wasm. For that we need:

  1. Lexer: DONE, written by hand by yours truly in late 2024.
  2. Parser: DONE, written mostly by an LLM.
  3. AstGen: DONE, written fully by an LLM.
  4. Sema: in progress.

Testing

Quick test:

zig build fmt-zig0 test-zig0

Static analysis (takes a while, run separately):

zig build lint-zig0

More elaborate (tries all compilers + static analysis + ReleaseSafe):

zig build all-zig0 -Doptimize=ReleaseSafe

Most elaborate, takes >10m:

zig build all-zig0 -Doptimize=ReleaseSafe -Dvalgrind |& grep -v Warning

Debugging tips

Test runs infinitely? Build the test program executable:

$ zig build test-zig0 -Dzig0-no-exec

And then run it, capturing the stack trace:

gdb -batch \
    -ex "python import threading; threading.Timer(1.0, lambda: gdb.post_event(lambda: gdb.execute('interrupt'))).start()" \
    -ex run \
    -ex "bt full" \
    -ex quit \
    zig-out/bin/test

You are welcome to replace -ex "bt full" with anything other of interest.

Float Handling

Float literals are parsed with strtold() (C11 standard, portable). On x86-64 Linux, long double is 80-bit extended precision (63 fraction bits).

When a float doesn't round-trip through f64, it's emitted as f128 (ZIR float128 instruction). The 80-bit extended value is converted to IEEE 754 binary128 encoding by bit manipulation — both formats share the same 15-bit exponent with bias 16383. The top 63 of binary128's 112 fraction bits come from the 80-bit value; the bottom 49 are zero-padded.

This means float128 literals lose ~49 bits of precision compared to the upstream Zig implementation (which uses native f128). This is acceptable because stage0 is a bootstrap tool — the real Zig compiler re-parses all source with full f128 precision in later stages. The test comparison mask in astgen_test.zig skips float128 payloads to account for this.

Previous approach used __float128/strtof128 (GCC/glibc extensions) for full precision, but these are not portable to TCC and other C11 compilers.