commit ad5fdfd09243c2d1bcbb95f3b1c5d99c1b7e5b3c
parent 645999b8a7c37d67a91aece265d39b44ac68d229
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 18 Jun 2026 16:27:44 -0700
Clear release correctness blockers
Diffstat:
38 files changed, 583 insertions(+), 199 deletions(-)
diff --git a/README.md b/README.md
@@ -12,9 +12,10 @@ It features:
- A single-pass C11 parser and code generator
- A JIT compiler, linker, and in-process executor
- An IR interpreter (`run --no-jit`)
-- A user-mode ELF emulator (per-basic-block JIT translation)
- A lightweight optimizer (`-O1`) with SSA construction, register allocation, and
local cleanup
+- `-O2` accepted as a v1 compatibility spelling for `-O1` (not a distinct
+ optimization level yet)
- Cross-compiling backends for aarch64, x86-64, and riscv64, plus WebAssembly and
a portable C-source backend
- Support for object files and executables (PE/COFF, ELF, Mach-O)
@@ -35,7 +36,7 @@ It features:
- A single multi-call binary, with an `install` command that drops per-tool
symlinks (hard links on Windows) into a directory for drop-in toolchain use
- Debug info generation and consumption (DWARF)
-- An interactive debugger
+- An interactive JIT debugger on the hard-green `test-dbg` host lanes
- Header dependency generation
- Reproducible builds
- Signed, content-addressed code distribution (`.kpkg`)
@@ -57,6 +58,6 @@ And kit-specific extensions:
Start with [`doc/DESIGN.md`](doc/DESIGN.md) — the entrypoint that covers kit's
design principles, layered architecture, and primary data flows, and indexes a
design doc for every major subsystem (frontends, codegen, IR, optimizer, arch
-backends, object formats, linker, JIT, emulator, debug info, debugger, runtime,
-driver, packaging, …). Forward-looking roadmaps live in [`doc/plan/`](doc/plan/).
+backends, object formats, linker, JIT, debug info, debugger, runtime, driver,
+packaging, …). Forward-looking roadmaps live in [`doc/plan/`](doc/plan/).
Code-interface detail lives in the public headers under `include/kit/`.
diff --git a/doc/CODEGEN.md b/doc/CODEGEN.md
@@ -26,7 +26,7 @@ Codegen has exactly two stable interfaces, stacked:
v
CgTarget realization
|-- NativeDirectTarget -O0 direct emit -> NativeOps + NativeTarget
- |-- CgIrRecorder -O1/-O2/interp -> recorded IR -> optimizer -> NativeTarget
+ |-- CgIrRecorder -O1/interp -> recorded IR -> optimizer -> NativeTarget
|-- C-source target / wasm (semantic, source-like output)
```
@@ -190,7 +190,7 @@ then conditionally wraps it:
- **`-O0`, native arch:** the backend's `make` returns a `NativeDirectTarget`
(see below). No IR is recorded; semantic ops emit machine code immediately.
-- **`-O1`/`-O2` or interpreter:** `session.c` wraps the base target with
+- **`-O1` (and `-O2`, currently an `-O1` alias) or interpreter:** `session.c` wraps the base target with
`opt_cgtarget_new` (`src/opt/opt.c`), which returns a `CgIrRecorder`
(`src/cg/ir_recorder.c`). Recording does not emit; at `kit_cg_finish` the optimizer
replays optimized IR. The recorder still holds the unwrapped native target so
diff --git a/doc/DBG.md b/doc/DBG.md
@@ -10,6 +10,19 @@ C11, like all of `src/`); the host primitives and the REPL live in the driver.
See [JIT.md](JIT.md) for how the image is produced and [DWARF.md](DWARF.md) for
the line/CFI/variable tables the source-level features consume.
+## V1 support boundary
+
+The V1 release claim is limited to host/arch pairs where `make test-dbg` runs
+hard-green. Today that scripted lane runs on Darwin and Linux aarch64 hosts
+(`arm64` / `aarch64`) and validates breakpoints, instruction and source-line
+stepping, backtraces, registers, variables, memory access, interrupt handling,
+and teardown behavior.
+
+Other arch hooks and host adapters may exist in the tree, but they are
+implementation work until their debugger lane proves the same behavior. The
+source-level features are claimed for `-O0 -g`; optimized-source debugging is
+not a V1 release claim.
+
## Layering
```
diff --git a/doc/DRIVER.md b/doc/DRIVER.md
@@ -2,7 +2,7 @@
The `kit` multitool is the toolchain's only executable: a single binary that
dispatches to ~27 named tools (compiler, assembler, linker, archive/object
-utilities, byte utilities, JIT runner, debugger, emulator, packager, and an
+utilities, byte utilities, JIT runner, debugger, packager, and an
`install` command that lays down the per-tool links). It is also the first and
canonical *consumer* of libkit — it depends only on the public API under
`include/kit/`, never on `src/`. Everything that the OS provides (heap, file
@@ -73,7 +73,7 @@ prototypes in `driver/driver.h`, the `cmd/<tool>.c`, and a Makefile stanza.
Exit-code convention across all tools: `0` success, `1` tool-reported error,
`2` bad usage. Help requests are detected by `driver_argv_wants_help`, which
stops scanning at a literal `--` so that a `--help` meant for a JITed program
-(`run`, `dbg`) or an emulated guest (`emu`) is not hijacked by the driver.
+(`run`, `dbg`) is not hijacked by the driver.
## The tools (cmd/)
@@ -83,15 +83,15 @@ tool reaches into compiler internals.
| Tool | Role |
|------|------|
-| `cc` | C compiler driver: compile, optionally link; preprocess (`-E`), dep-emit (`-M*`), `-shared`. GCC flag subset. Resolves `-l`/`-L` to concrete archive paths. |
+| `cc` | C compiler driver: compile, optionally link; preprocess (`-E`), dep-emit (`-M*`), ELF `-shared`. GCC flag subset. Resolves `-l`/`-L` to concrete archive paths. |
| `check` | Run the C frontend checks with no code emission. |
-| `build-exe` | Kit-native build verb: compile a polyglot source set (C / asm / toy / wasm, per file) in memory and link it — with any `.o`/`.a`/`.so` inputs — into an executable. No intermediate files. |
-| `build-lib` | Compile a polyglot source set in memory into a static `.a` (default) or, with `-dynamic`, a shared library. |
+| `build-exe` | Kit-native build verb: compile a polyglot source set (C / asm / wasm, per file) in memory and link it — with any `.o`/`.a`/`.so` inputs — into an executable. No intermediate files. |
+| `build-lib` | Compile a polyglot source set in memory into a static `.a` (default) or, with `-dynamic`, an ELF shared library. |
| `build-obj` | Compile sources to one object (or `--emit=asm\|c\|ir`, or `-fsyntax-only` check); multiple sources combine into one relocatable object (`ld -r`). The kit-native replacement for the retired `compile` tool. |
| `install` | Lay down per-tool links (symlinks; hard links on Windows) in a target dir so the toolchain works under bare names (`cc`, `ld`, `nm`, …). Default set is the toolchain + standard-named byte utils; `--all` / explicit names override. |
| `cpp` | Standalone preprocessor (alias for `cc -E` without link scaffolding). |
| `as` | Assemble one GAS-subset text source to a relocatable object. |
-| `ld` | Link objects/archives into an executable, shared library, or relocatable object; parses `-T` scripts into structured form. |
+| `ld` | Link objects/archives into an executable, ELF shared library, or relocatable object; parses `-T` scripts into structured form. |
| `ar` / `ranlib` | Create/modify/list/extract `ar` archives; refresh the symbol index. |
| `strip` / `objcopy` | Drop debug/symbols; rename/remove sections, reformat. |
| `objdump` / `nm` / `size` | Inspect sections, symbols, disassembly, relocations, sizes. |
@@ -105,13 +105,12 @@ tool reaches into compiler internals.
| `disas` | Disassemble a raw, headerless byte buffer (file/stdin/inline `-x` hex) for a `-target` arch. |
| `mc` | Assemble one instruction and show its encoding (llvm-mc style); lists any relocations. |
| `run` | JIT-compile inputs and call the entry symbol in-process. |
-| `dbg` | Interactive JIT debugger (REPL over a `KitDebugSession`). |
-| `emu` | Run a guest user-mode ELF (aarch64/riscv64) via per-block JIT translation. |
+| `dbg` | Interactive JIT debugger on hard-green `test-dbg` host lanes (REPL over a `KitDebugSession`). |
| `cas` / `pkg` | Content-addressed store and signed `.kpkg` distribution. |
-`run`, `dbg`, and `emu` share the `--`-terminated argv convention: flags before
-`--` configure the tool, tokens after `--` become the JITed program's / guest's
-argv. `cc` and `run` overlap heavily on input shape and the preprocessor flag
+`run` and `dbg` share the `--`-terminated argv convention: flags before
+`--` configure the tool, tokens after `--` become the JITed program's argv.
+`cc` and `run` overlap heavily on input shape and the preprocessor flag
family — that overlap is exactly what `driver/lib/` factors out.
`cc` vs `build-*`: `cc` is the GCC-compatible C driver — a drop-in `cc`/`clang`
@@ -129,6 +128,16 @@ flags) that apply to the whole build, and a small scopable set (`-I`/`-isystem`/
and the same language-neutral per-source compile step
(`driver/lib/compile_engine.c`).
+For v1, `-O0` and `-O1` are the real optimization levels. `-O2` is accepted by
+the driver and public CG API as a compatibility spelling, but it aliases `-O1`
+and does not enable a separate optimizer schedule. This is intentional release
+policy and may change after v1.
+
+The emulator command and Toy frontend are internal for v1: they may remain
+compiled for development and regression tests, but they are not listed in
+top-level help, installed by `install --all`, or presented as public release
+surface.
+
`run` doubles as a `#!` script interpreter so a C file can be made executable
and run directly. The kernel's shebang mechanism appends the script path *and*
the user's arguments after the interpreter's flags, with no way to inject a
diff --git a/doc/IR.md b/doc/IR.md
@@ -74,14 +74,14 @@ frontend
-> CgTarget (semantic codegen interface)
|-> direct native target (O0 emit)
|-> direct C-source target (--emit=c)
- \-> IR recorder -> CgIrModule (O1/O2, interpreter)
+ \-> IR recorder -> CgIrModule (O1, O2-as-O1, interpreter)
|-> opt: derive Func (CFG/SSA/MIR) -> native emit
\-> opt: derive Func (reduced) -> interpreter
```
`KitCg` lowers the frontend's stack/lvalue source operations into flat
`CgTarget` calls. At O0 those calls hit a direct target and become code right
-away. At O1/O2 and under the interpreter they hit the recorder and become a
+away. At O1, O2-as-O1, and under the interpreter they hit the recorder and become a
`CgIrModule`. The recorder is created by the optimizer (src/opt/opt.c calls
`cg_ir_recorder_new`); it notifies the optimizer per completed function and at
finalize through callbacks so cross-function work (inlining, reachability,
diff --git a/doc/LINK.md b/doc/LINK.md
@@ -24,6 +24,11 @@ sysroots) lives entirely in the driver — the library boundary is
byte-buffer-shaped. Every bytes input is read through `Compiler.env->
file_io` by the driver before it reaches the linker.
+For v1, shared-library **creation** is supported only for ELF targets. Mach-O
+and COFF/PE image and DSO readers still support executable links against
+existing dylibs/DLL import surfaces, but the driver rejects non-ELF
+`-shared`/`-dynamic` output requests.
+
The two central abstractions:
- **`Linker`** — the mutable accumulator. Holds the registered inputs
diff --git a/doc/OBJ.md b/doc/OBJ.md
@@ -312,6 +312,9 @@ linker's `-l` path: `read_macho_dso` (MH_DYLIB exports) and `tbd_read.c` (Apple
model, the `__DWARF` segment section-name spellings — are concentrated in
`obj_secnames.c`/`obj_tls.c` and the writer, not the backends.
+V1 reads Mach-O dylib metadata for executable links, but does not create Mach-O
+shared libraries; driver `-shared`/`-dynamic` output is ELF-only.
+
Mach-O objects deliberately carry **no `__eh_frame`** (the `emits_eh_frame`
target gate in `src/api/core.c` excludes `KIT_OBJ_MACHO`). arm64 Mach-O has no
pcrel-32 data relocation for a DWARF FDE pc-begin, and ld64's legacy
@@ -344,6 +347,10 @@ string, and `Characteristics`→`SecKind` helpers shared by the `.obj`, DSO, and
image readers. `read_dso.c` walks raw PE DLL export directories (and forwarder
ENT entries, surfaced as defined symbols so the OS loader chases the chain at
runtime).
+
+V1 reads PE/COFF DLL and import metadata for executable links, but does not
+create PE DLLs; driver `-shared`/`-dynamic` output is ELF-only.
+
`archive.c` implements the registry's archive-ingestion hooks: it classifies
import-library members, routing Microsoft short-import records
(`Sig1=0, Sig2=0xFFFF`) through `read_coff` to synthesize the imported symbols
diff --git a/doc/OPT.md b/doc/OPT.md
@@ -296,9 +296,8 @@ implemented, but it is not on the shipped code path: **`opt_cleanup` has no
caller.** The finalize sweep (`opt_whole_module_finalize`) always runs the O1
native pipeline (`opt_o1_native_prepare`/`_finish`) regardless of the requested
`opt_level`, so no compilation ever selects the SSA schedule and every
-`opt_level >= 1` request runs the O1 native path. (`-O2` therefore produces the
-same output as `-O1` today; the level is recorded on `OptImpl` but only gates
-whole-program mode, which `-O1` already enables.)
+`opt_level >= 1` request runs the O1 native path. Public `opt_level == 2` is
+normalized to `1`, so `-O2` produces the same output as `-O1` today.
The rationale for keeping it parked is isolation. Keeping the O2 schedule
defined and its passes maintained means the SSA representation and its
diff --git a/doc/WASM.md b/doc/WASM.md
@@ -20,7 +20,7 @@ module model serves both:
and JITs/links like any other frontend. The module's runtime state — memory,
globals, tables, imports — is reified as an explicit instance struct.
-- **Wasm as target.** C or toy is compiled *to* a Wasm module via
+- **Wasm as target.** C is compiled *to* a Wasm module via
`KIT_ARCH_WASM` / `KIT_OBJ_WASM`. The backend in `src/arch/wasm` is a
codegen target that records into IR and replays into a private Wasm emitter,
producing a tool-conventions-shaped `.wasm` file.
@@ -28,6 +28,28 @@ module model serves both:
Neither direction builds a private reader or writer: both go through the one
`src/wasm` layer. That non-duplication is the central design constraint.
+## V1 support boundary
+
+The shipped target boundary is intentionally narrow. `wasm32` is the only Wasm
+output target for v1. `wasm64` remains a reserved triple/ISA spelling so the
+parser can name it, but target construction rejects it with an explicit
+unsupported diagnostic.
+
+Wasm/WAT input is supported as a source language and `kit run` can execute those
+modules with an explicit instance and deny-by-default host imports. The WASI
+surface is partial WASI Preview1: the runner binds only the configured subset of
+imports and host resources selected by `--wasm-imports=wasi` and the
+`--wasm-*` resource flags. Unsupported WASI calls are not treated as a complete
+WASI implementation.
+
+For C-to-Wasm output, same-invocation multi-source `wasm32` is supported through
+the source-batch path (`build-obj -target wasm32-none ...` or
+`build-exe -target wasm32-none ...`). Those sources are merged before the final
+module is emitted. Separate-object/static Wasm linking is not supported in v1:
+`build-exe -target wasm32-none` rejects `.o`, `.a`, `-l`, DSO, and framework
+inputs, and `emit_wasm` does not create relocatable Wasm objects with
+`linking`/`reloc.*` metadata.
+
```
src/wasm/ (format mechanics + WasmModule model)
decode.c encode.c wat.c validate.c insn.c module.c
@@ -160,7 +182,9 @@ function declarations, active tables/elements for `call_indirect`, start
functions, growable single-memory state, bulk-memory ops
(`memory.copy`/`fill`/`init`, `data.drop`, the table equivalents) with
bounds-check prologues, and non-trapping float-to-int conversion. Bulk ops and
-non-trapping conversions are gated behind their `WasmFeatureSet` bits.
+non-trapping conversions are gated behind their `WasmFeatureSet` bits. This is
+frontend/runtime coverage, not a claim of complete WASI or every post-MVP Wasm
+proposal.
**Host imports.** When a frontend-lowered module declares imports, an embedder
binds them by name through the public API in `include/kit/wasm.h`
@@ -252,6 +276,8 @@ The object backend is intentionally small and one-directional today. `emit.c`'s
is attached, writes a bare magic+version header). It does **not** synthesize a
relocatable object: `emit_wasm` produces a single-TU *final module* with no
`linking`/`reloc.*` custom sections and no separate object-metadata structure.
+Multi-source output is handled before this point by the source-batch build path,
+not by linking independently emitted Wasm objects.
`read.c`'s `read_wasm` is the reverse glue used by the linker/objdump path
([LINK.md](LINK.md)): it mirrors each binary section into a format-neutral
diff --git a/doc/plan/RELEASE.md b/doc/plan/RELEASE.md
@@ -82,7 +82,7 @@ directory, then inspected from the saved logs.
- [ ] Deferred portability lanes are listed with owner/status:
`make test-cross DEPTH=full`, `make test-selfhost DEPTH=smoke`, and
`make test-selfhost DEPTH=full`.
-- [ ] Release-gate configuration has `emu` and user-facing Toy hidden/disabled,
+- [x] Release-gate configuration has `emu` and user-facing Toy hidden/disabled,
while internal Toy tests remain available if still useful.
- [ ] No public v1 doc or help text claims a feature outside this checklist.
@@ -91,23 +91,23 @@ directory, then inspected from the saved logs.
These block v1 until fixed, removed from the public claim, or explicitly
converted into a non-public unsupported feature.
-- [ ] Fix the latent `kit run` uninitialized-stack read.
-- [ ] Fix the c_target Wasm `memory.grow` backing-store bug.
-- [ ] Fix aa64 optimized inline-asm staging panics or remove the affected
+- [x] Fix the latent `kit run` uninitialized-stack read.
+- [x] Fix the c_target Wasm `memory.grow` backing-store bug.
+- [x] Fix aa64 optimized inline-asm staging panics or remove the affected
optimized inline-asm claim.
-- [ ] Fix x64 optimized inline-asm staging panics or remove the affected
+- [x] Fix x64 optimized inline-asm staging panics or remove the affected
optimized inline-asm claim.
-- [ ] Make `-O2` behavior explicit everywhere: alias to `-O1`, not a separate
+- [x] Make `-O2` behavior explicit everywhere: alias to `-O1`, not a separate
optimizer level.
-- [ ] Hide or remove user-facing `emu` exposure from README, driver help,
+- [x] Hide or remove user-facing `emu` exposure from README, driver help,
install defaults, and release docs.
-- [ ] Hide Toy from user-facing docs, help, examples, and install defaults.
-- [ ] Reject or hide non-ELF shared-library creation paths for `cc`, `ld`, and
+- [x] Hide Toy from user-facing docs, help, examples, and install defaults.
+- [x] Reject or hide non-ELF shared-library creation paths for `cc`, `ld`, and
`build-lib`; keep ELF DSO creation enabled and tested.
-- [ ] Update docs/help/tests for the Wasm boundary: same-invocation multi-source
+- [x] Update docs/help/tests for the Wasm boundary: same-invocation multi-source
`wasm32` is supported; separate-object/static Wasm linking, wasm64, and
complete WASI are not.
-- [ ] Keep debugger support claims hard-green only. Any host/arch pair without
+- [x] Keep debugger support claims hard-green only. Any host/arch pair without
proven breakpoints, stepping, backtrace, registers, variables, memory
access, interrupt, and teardown is removed from the v1 claim.
@@ -117,18 +117,18 @@ converted into a non-public unsupported feature.
- [ ] README, `kit --help`, per-tool help, and install output match the V1 public
surface exactly.
-- [ ] [../DRIVER.md](../DRIVER.md) documents `-O2` as a temporary `-O1` alias,
+- [x] [../DRIVER.md](../DRIVER.md) documents `-O2` as a temporary `-O1` alias,
ELF-only shared-library creation, and hidden `emu`/Toy policy.
-- [ ] [../LINK.md](../LINK.md) and [../OBJ.md](../OBJ.md) distinguish ELF DSO
+- [x] [../LINK.md](../LINK.md) and [../OBJ.md](../OBJ.md) distinguish ELF DSO
creation from executable links against DSOs on all object formats.
-- [ ] [../WASM.md](../WASM.md) states the `wasm32`, WAT/Wasm input, runtime,
+- [x] [../WASM.md](../WASM.md) states the `wasm32`, WAT/Wasm input, runtime,
partial-WASI, and same-invocation multi-source boundary.
- [ ] [../BUILD_COORDINATOR.md](../BUILD_COORDINATOR.md),
[../DISTRIBUTE.md](../DISTRIBUTE.md), and release packaging docs cover
`build`, `cas`, `pkg`, `install`, and `update`.
- [ ] [../KERNEL.md](../KERNEL.md) matches the shipped `image` and `cpio` tool
surface.
-- [ ] [../DBG.md](../DBG.md) contains only debugger platforms that are
+- [x] [../DBG.md](../DBG.md) contains only debugger platforms that are
hard-green.
### Toolchain and linker
@@ -138,20 +138,20 @@ converted into a non-public unsupported feature.
- [ ] ELF DSO creation passes targeted tests for supported ELF targets.
- [ ] ELF executable links against DSOs pass targeted tests for supported ELF
targets.
-- [ ] Non-ELF shared-library creation rejects clearly in `cc`, `ld`, and
+- [x] Non-ELF shared-library creation rejects clearly in `cc`, `ld`, and
`build-lib`.
-- [ ] `-O2` alias behavior is covered by driver/help tests and optimizer tests.
+- [x] `-O2` alias behavior is covered by driver/help tests and optimizer tests.
- [ ] aa64, x64, rv64, rv32, and arm32 compile/link claims are covered by the
hard release gates or explicitly scoped as freestanding smoke lanes.
### WebAssembly and WASI
-- [ ] Targeted `wasm32` single-source C output tests pass.
-- [ ] Targeted same-invocation multi-source `wasm32` tests pass.
+- [x] Targeted `wasm32` single-source C output tests pass.
+- [x] Targeted same-invocation multi-source `wasm32` tests pass.
- [ ] Wasm/WAT input and `kit run` execution tests pass.
- [ ] WASI Preview1 supported imports are listed and tested; unsupported calls
fail with feature-naming diagnostics.
-- [ ] Separate-object/static Wasm linking and wasm64 attempts reject clearly.
+- [x] Separate-object/static Wasm linking and wasm64 attempts reject clearly.
### Utility and distribution tools
diff --git a/doc/plan/TODO.md b/doc/plan/TODO.md
@@ -9,37 +9,6 @@ Add new deferred fixes below as they are discovered.
## Known bugs / correctness gaps
-- **Latent uninitialized-stack-variable read in the `kit run` in-process compile+link
- pipeline (layout-sensitive heisenbug). MITIGATED by `-ftrivial-auto-var-init=zero`;
- not yet root-caused.** `kit run` on an FP program flakily dies with `link: undefined
- reference to '__muldf3'` (or `__divdf3` / `__floatsidf` / `__fixdfdi` — whichever
- soft-float compiler-rt helper the program needs). **Pre-existing and DORMANT at HEAD**
- (0 failures in 240+ runs), but woken by memory-layout shifts: e.g. the D.3+D.4
- obj-format dedups trip it (~10–100% per build, varying run-to-run) when built WITHOUT
- the hardening flag, while 4 dummy padding fields in the same struct do not — a latent
- landmine, not caused by any one refactor. **`-ftrivial-auto-var-init=zero` (mk/flags.mk
- `AUTO_INIT_CFLAGS`) fully suppresses it:** a clean build with D.3+D.4 + the flag shows
- 0/120 genuine link-error flakes and `test-toy` 1394 pass / 0 fail across repeated
- runs, so the read is from an uninitialized *stack* auto. The flag is the standard
- zero-init hardening and is now on; this entry stays open because the flag MASKS the UB
- rather than fixing the specific read. (Note: an earlier "8/80 with the flag" reading
- was a probe bug — case `140_fp_callee_save_bottom_frame` has `.expected`=964, which
- wraps to exit code 196 under an 8-bit exit-code compare; it is not a real failure.)
- Characterization (all verified):
- - Only manifests in the multi-source in-process pipeline. Compiling `rt/lib/fp/fp.c`
- (which defines the missing helper) *alone* is 100% deterministic (50/50 clean);
- the flake needs many sources compiled+linked in one process.
- - **ASAN + UBSan never fire** → it is an uninitialized-memory READ (those sanitizers
- don't detect uninit reads). It is a STACK auto: `-ftrivial-auto-var-init=zero`
- (which only touches automatic storage) fully suppresses it. `segvec` zeroes its
- slots and `obj_symbol_make` is clean, so the ObjSym/LinkSymbol tables are NOT the
- source — look at per-source-compile locals in the driver pipeline.
- - Real-fix path: the definitive tool is **MemorySanitizer**, unsupported on
- macOS/arm64. On the arm64 Linux container (scripts/linux_bootstrap.sh), build kit
- under `-fsanitize=memory`, TEMPORARILY drop `AUTO_INIT_CFLAGS`, and run a flaky FP
- repro (`kit run -O0 test/toy/cases/54_scalar_intrinsics.toy` in a loop) to get the
- exact file:line, then initialize that local. The hardening flag can stay regardless.
-
- **`-ftrivial-auto-var-init=pattern` in `kit cc`.** `=zero` and `=uninitialized`
are implemented: the C frontend zero-inits every automatic variable that has no
explicit initializer (`KitCodeOptions.trivial_auto_var_init`, threaded through
@@ -51,49 +20,16 @@ Add new deferred fixes below as they are discovered.
FP bit pattern), i.e. generalizing `zero_init_at` to a fill value. Lower priority:
the hardening uses `=zero`.
-- **c_target `memory.grow` backing-store bug.** `memory_grow_large/C` fails
- (`expected 42 got 139`, SIGSEGV): a `(memory 1 300)` module grows to 300 pages
- then stores/loads at ~18.7 MB and segfaults. Pre-existing (confirmed failing
- identically at `de9bface`); surfaces in `test-cbackend`'s wasm-front sub-suite.
- **Diagnosis (do NOT "add realloc to the lowering"):** the `memory.grow` lowering in
- `lang/wasm/cg.c` is backend-shared and correct — the native lanes pass
- `memory_grow_large`. It just bumps `memory.pages`; the backing store is allocated
- by the *host/instantiation*, and native pre-allocates `max_pages` of linear memory
- (so a store at page 299 is in-bounds) while the C-backend host evidently allocates
- only the initial `min_pages`. The fix is in the C-backend wasm memory instantiation
- (allocate `max_pages`, matching native), not the shared lowering — adding a realloc
- there would wrongly perturb the native path.
-
-- **aa64/x64 inline-asm operand-staging gap (A.3).** A register-constrained asm
- operand that regalloc could not keep in a hard register (e.g. an address-taken
- / spilled local) is staged to a scratch reg and succeeds on rv64, but **panics**
- on aa64/x64 (`"register asm operand not in a register"`). Three gaps in the
- optimizer-path `*_asm_block_native`: (a) register-constrained **outputs** in a
- non-reg loc have no fallback on aa64 (`src/arch/aa64/native.c:4883`) / x64
- (`:4021`), only rv64 (`src/arch/riscv/native.c:3780`); (b) **FP-constrained
- inputs** (`"w"` aa64, `"x"/"v"` x64) in a non-reg loc; (c) x64 **`"q"`** inputs
- (input-stage test is raw `body[0]=='r'`, so `q`→INT,allowed=0 skips staging then
- panics). rv64's extra staging branch is correct (it has dedicated stage regs
- `RV_TMP2/3`+`RV_FTMP0/1`). Fix aa64/x64 to match, with test cases — this changes
- which programs compile vs panic, so it must be a reviewed change, NOT folded into
- a dedup. Unblocks A.2 (below). **Blocker (register budget):** the gap is not a
- localized edit — rv64 has *dedicated* stage regs separate from its mem-scratch,
- but aa64 has only `AA_TMP0/1` (x16/x17, int, which double as the `"m"` mem-scratch)
- and *no* FP scratch, and x64 likewise has only `R10/R11` + `XMM14/15` doubling as
- mem-scratch. Matching rv64 means either permanently reserving more scratch regs on
- aa64/x64 (a codegen-quality cost on every function) or collision-aware sharing of
- the existing scratch between mem-staging and operand-staging — a register-policy
- decision, hence "reviewed change."
-
## Deferred dedups & abstraction cleanups ("use the shared seam")
- **A.2 — optimizer-path inline-asm dedup.** The three `aa_/x64_/rv_asm_block_native`
share an out/in/bind/run/store skeleton but diverge by *policy* (staging coverage +
structurally different scratch-register models: aa64/x64 reuse 2 mem-scratch regs via
a shared `ntmp`; rv64 uses 4 dedicated stage regs). A hooks dedup would relocate, not
- unify, behavior and add miscompile risk in a hot path. Do **A.3** first (make the three
- staging policies identical), then a shared `native_asm_bind_optimizer_operands` becomes
- safe. (The -O0 direct path is already shared via `native_asm_bind_direct_operands`.)
+ unify, behavior and add miscompile risk in a hot path. Now that the aa64/x64
+ staging gaps are closed, a shared `native_asm_bind_optimizer_operands` can be
+ considered. (The -O0 direct path is already shared via
+ `native_asm_bind_direct_operands`.)
- **E.3 — OS-neutral env code duplicated** between `driver/env/posix.c` and `windows.c`
(stdio writer + thunks, `env→{context,jit_host,dbg_host}` trio, dir-handle structs +
read/close, `read_stdin` grow/shrink). Move to `driver/env/common.c`. **Blocked on a
@@ -194,12 +130,15 @@ doc at `HEAD~`).
- **Object backend** (largest gap): `src/obj/wasm` lacks relocatable-object +
linker-metadata support at ELF/Mach-O/COFF parity.
-- **Static linker**: no Wasm linker — `kit_link_exe` always builds a native
- `Linker`; need multi-TU Wasm merge + relocation apply.
-- **Feature gaps**: cross-TU references, atomics, wrapper ABI; frontend lowering
- beyond the staged MVP; validator diagnostics for unsupported proposals.
-- **wasm64 + WASI**: recognized-but-unsupported until the wasm32 object/link path
- exists. **Cleanup**: move the shared Wasm core `lang/wasm/` → `src/wasm/`.
+- **Static linker**: no separate-object/static Wasm linker — `kit_link_exe`
+ always builds a native `Linker`. Same-invocation source batches are merged
+ before final module emission; independently emitted Wasm objects still need
+ linker metadata + relocation apply.
+- **Feature gaps**: atomics, wrapper ABI; frontend lowering beyond the staged
+ MVP; validator diagnostics for unsupported proposals.
+- **wasm64 + WASI**: wasm64 is a reserved spelling rejected by target
+ construction, and WASI remains partial until the wasm32 object/link path
+ grows. **Cleanup**: move the shared Wasm core `lang/wasm/` → `src/wasm/`.
### Windows x64 self-host + bootstrap (was `windows.md`)
diff --git a/driver/cmd/build.c b/driver/cmd/build.c
@@ -18,7 +18,7 @@
/* `kit build-exe` / `build-lib` / `build-obj` — the kit-native build verbs.
*
- * Each command is polyglot (C / asm / toy / wasm resolved per file), compiles
+ * Each command is polyglot (C / asm / wasm resolved per file), compiles
* entirely in memory, and writes no intermediate files. They share one
* parse+run parameterized by output kind:
*
@@ -165,7 +165,7 @@ static int build_lang_from_name(BuildOptions* o, const char* name,
KitLanguage* out) {
/* Resolve off the compile-time default frontend set (no compiler exists at
* arg-parse time). Value-equivalent to the former explicit map:
- * c->C, asm/s->ASM, toy->TOY, wasm/wat->WASM. */
+ * c->C, asm/s->ASM, wasm/wat->WASM. */
KitLanguage lang = kit_frontend_registry_language_for_name(o->frontends, name);
if (lang == KIT_LANG_UNKNOWN) return 1;
*out = lang;
@@ -284,7 +284,7 @@ static int build_classify_positional(BuildOptions* o, const char* a) {
return 0;
}
/* Otherwise a source: a recognized source suffix, or any file at all when a
- * language is forced in scope (so `-x toy mykernel` compiles an extensionless
+ * language is forced in scope (so `-x c mykernel` compiles an extensionless
* or odd-suffix file). */
if (build_is_source(o, a) || build_scope_forced_lang(o, o->cur_group) >= 0) {
BuildSource* s = &o->sources[o->nsources];
@@ -528,7 +528,7 @@ static int build_parse(int argc, char** argv, BuildOptions* o) {
if (driver_streq(a, "-O2") || driver_streq(a, "-O") ||
driver_streq(a, "-O3") || driver_streq(a, "-Os") ||
driver_streq(a, "-Oz") || driver_streq(a, "-Ofast")) {
- o->opt_level = 2;
+ o->opt_level = 1;
continue;
}
if (driver_streq(a, "-Werror") || driver_strneq(a, "-Werror=", 8)) {
@@ -2413,6 +2413,10 @@ static int build_main(int argc, char** argv, int kind, const char* tool,
o.shared = (o.kind == BUILD_OUT_LIB && o.dynamic);
if (o.shared && !o.pic_explicit) o.target.pic = KIT_PIC_PIC;
+ if (o.shared && o.target.obj != KIT_OBJ_ELF) {
+ driver_errf(tool, "-shared output is supported only for ELF targets in v1");
+ goto done;
+ }
if (o.shared && o.target.pic == KIT_PIC_NONE) {
driver_errf(tool, "-shared requires PIC input; remove -fno-pic/-static");
goto done;
@@ -2565,11 +2569,14 @@ void driver_help_build_exe(void) {
"DESCRIPTION\n"
" Compiles registered source languages (language per file) in memory\n"
" and links them with any .o/.a/.so inputs into one executable. No\n"
- " intermediate files.\n"
+ " intermediate files. For -target wasm32-none, inputs must be\n"
+ " sources supplied in this invocation; .o/.a/-l Wasm linking is not\n"
+ " a v1 feature.\n"
"\n"
"OPTIONS (selection)\n"
" -o PATH Output (default a.out / a.exe)\n"
- " -O0 -O1 -O2 -g Optimization / debug info\n"
+ " -O0 -O1 -O2 -g Optimization / debug info (-O2 aliases "
+ "-O1)\n"
" -target TRIPLE Cross-compile target\n"
" -arch ARCH Darwin-style target architecture\n"
" -platform_version P MIN SDK\n"
@@ -2590,21 +2597,23 @@ void driver_help_build_lib(void) {
driver_printf(
"%.*s",
KIT_SLICE_ARG(KIT_SLICE_LIT(
- "kit build-lib — build a static library (.a)\n"
+ "kit build-lib — build a static library (.a) or ELF shared library\n"
"\n"
"USAGE\n"
" kit build-lib -o LIB.a [options] sources...\n"
+ " kit build-lib -dynamic -o LIB.so [options] sources...\n"
"\n"
"DESCRIPTION\n"
" Compiles a polyglot source set in memory and archives the "
"objects\n"
- " into a static library (.a) with a symbol index. Dynamic/shared\n"
- " libraries are not yet supported.\n"
+ " into a static library (.a), or links an ELF shared library with\n"
+ " -dynamic/-shared. Non-ELF shared-library output is rejected.\n"
"\n"
"OPTIONS (selection)\n"
" -o PATH Output archive (required)\n"
" -fPIC Position-independent code\n"
- " -O0 -O1 -O2 -g Optimization / debug info\n"
+ " -O0 -O1 -O2 -g Optimization / debug info (-O2 aliases "
+ "-O1)\n"
" -flto Link-time optimization for source inputs\n"
" -target TRIPLE Cross-compile target\n"
" --group [flags] -- sources... Scope compile flags to sources\n"
@@ -2624,8 +2633,10 @@ void driver_help_build_obj(void) {
"DESCRIPTION\n"
" Compiles each source (registered language by suffix or -x) to an\n"
" object. Multiple sources with --emit=obj combine into one\n"
- " relocatable object (ld -r). The kit-native replacement for the\n"
- " retired `compile` tool.\n"
+ " relocatable object (ld -r), except -target wasm32-none merges the\n"
+ " same source batch into one final wasm32 module. Separate Wasm\n"
+ " object/static linking is not a v1 feature. The kit-native\n"
+ " replacement for the retired `compile` tool.\n"
"\n"
"OPTIONS\n"
" -o PATH Output (default <base>.o; required for a\n"
@@ -2633,7 +2644,8 @@ void driver_help_build_obj(void) {
" --emit=obj|asm|c|ir Output form (ir requires -O1+)\n"
" -S Alias for --emit=asm\n"
" -fsyntax-only Check only; write no output\n"
- " -O0 -O1 -O2 -g Optimization / debug info\n"
+ " -O0 -O1 -O2 -g Optimization / debug info (-O2 aliases "
+ "-O1)\n"
" -flto Link-time optimization for multi-source "
"obj\n"
" -target TRIPLE Cross-compile target\n"
diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c
@@ -21,7 +21,7 @@
* without -c compiles all C sources, links any .o/.a inputs alongside, and
* emits an executable. The flag surface is a GCC subset:
*
- * -c -E -o -O0/1/2 -g
+ * -c -E -o -O0/1 (-O2 aliases -O1 for v1) -g
* -fsyntax-only
* -I -isystem -D -U
* -M -MM -MD -MMD -MF -MT -MQ -MP
@@ -87,7 +87,7 @@ typedef struct CcOptions {
int emit_c_source; /* --emit=c */
int emit_ir; /* --emit=ir */
int emit_asm_source; /* -S */
- int opt_level; /* -O0/-O1/-O2 */
+ int opt_level; /* -O0/-O1; -O2 aliases -O1 */
int debug_info; /* -g */
int function_sections; /* -ffunction-sections */
int data_sections; /* -fdata-sections */
@@ -179,7 +179,7 @@ void driver_help_cc(void) {
".o\n"
" kit cc -E [options] input.c preprocess to -o\n"
" kit cc -fsyntax-only [options] inputs... check only\n"
- " kit cc -shared [options] inputs... link a shared "
+ " kit cc -shared [options] inputs... link an ELF shared "
"library\n"
" kit cc -M|-MM [options] input.c print header deps; "
"no "
@@ -198,6 +198,8 @@ void driver_help_cc(void) {
"source\n"
" --emit=ir -O1 [options] input.c emit semantic IR "
"dump\n"
+ " -O0 | -O1 | -O2 optimization "
+ "(-O2 aliases -O1 for v1)\n"
" -flto link-time "
"optimization for all source inputs\n"
"\n"
@@ -672,13 +674,13 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
continue;
}
if (driver_streq(a, "-O2")) {
- o->opt_level = 2;
+ o->opt_level = 1;
continue;
}
if (driver_streq(a, "-O") || driver_streq(a, "-O3") ||
driver_streq(a, "-Os") || driver_streq(a, "-Oz") ||
driver_streq(a, "-Ofast")) {
- o->opt_level = 2;
+ o->opt_level = 1;
continue;
}
@@ -1299,6 +1301,11 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
"(shared-library LTO output is not exercised)");
return 1;
}
+ if (o->shared && o->target.obj != KIT_OBJ_ELF) {
+ driver_errf(CC_TOOL,
+ "-shared output is supported only for ELF targets in v1");
+ return 1;
+ }
if (o->shared && o->target.pic == KIT_PIC_NONE) {
driver_errf(CC_TOOL, "-shared requires PIC input; remove -fno-pic/-static");
return 1;
diff --git a/driver/cmd/dbg.c b/driver/cmd/dbg.c
@@ -85,7 +85,7 @@ void driver_help_dbg(void) {
"kit dbg — interactive JIT debugger\n"
"\n"
"USAGE\n"
- " kit dbg [options] [input.{c,toy,s} ...] [-- prog-arg ...]\n"
+ " kit dbg [options] [input.{c,s,wat,wasm} ...] [-- prog-arg ...]\n"
"\n"
"DESCRIPTION\n"
" Mirrors `kit run` for compile flags and argv shape, but "
@@ -96,6 +96,8 @@ void driver_help_dbg(void) {
" finish, backtrace, registers, locals/args, variable read/write,\n"
" and raw memory examine. -g is forced on so source lines and\n"
" variable locations are available at runtime.\n"
+ " V1 support is limited to host/arch lanes where test-dbg runs\n"
+ " hard-green: Darwin/Linux aarch64 hosts today.\n"
"\n"
" Anything after `--` is passed to the JITed program as argv.\n"
" With no input files, dbg starts an empty JIT session; append "
@@ -103,10 +105,11 @@ void driver_help_dbg(void) {
" with `jit` or evaluate expressions directly from the REPL.\n"
"\n"
"COMPILE OPTIONS\n"
- " -O0 -O1 -O2 Optimization level (default -O0)\n"
+ " -O0 -O1 -O2 Optimization level; -O2 aliases -O1 "
+ "(default -O0)\n"
" -g Emit DWARF (forced on)\n"
" -e SYMBOL Entry symbol (default `main`)\n"
- " -x LANG Default REPL language: c, toy, asm, wasm/wat\n"
+ " -x LANG Default REPL language: c, asm, wasm/wat\n"
" --language LANG Same as -x\n"
" -I DIR Add quoted-include search path\n"
" -isystem DIR Add system-include search path\n"
@@ -151,7 +154,10 @@ void driver_help_dbg(void) {
" info reg, info registers dump registers\n"
" info locals | info args list locals / args at current PC\n"
" info functions [PATTERN] list JIT functions matching PATTERN\n"
- " info variables [PATTERN] list JIT globals matching PATTERN\n"
+ " info variables [PATTERN] list JIT globals matching PATTERN\n")));
+ driver_printf(
+ "%.*s",
+ KIT_SLICE_ARG(KIT_SLICE_LIT(
"\n"
"BATCH / SCRIPTING\n"
" --script FILE execute debugger commands from FILE "
@@ -281,7 +287,7 @@ static int dbg_parse(int argc, char** argv, DbgOpts* o) {
continue;
}
if (driver_streq(a, "-O2")) {
- o->opt_level = 2;
+ o->opt_level = 1;
continue;
}
@@ -1836,7 +1842,7 @@ static void dbg_cmd_jit(DbgState* s, const char* rest) {
if (dbg_parse_jit_lang_arg(s, rest, &lang, &input_name, &p) != 0) return;
if (*p != '{') {
- dbg_errf(s, "usage: jit [c|toy|asm|name.ext] { ... }");
+ dbg_errf(s, "usage: jit [c|asm|name.ext] { ... }");
return;
}
++p;
@@ -1895,7 +1901,7 @@ static void dbg_cmd_edit(DbgState* s, const char* rest) {
if (dbg_parse_jit_lang_arg(s, rest, &lang, &input_name, &p) != 0) return;
if (*p) {
- dbg_errf(s, "usage: edit [c|toy|asm|name.ext]");
+ dbg_errf(s, "usage: edit [c|asm|name.ext]");
return;
}
if (!driver_edit_temp(
@@ -1944,7 +1950,7 @@ static void dbg_cmd_language(DbgState* s, const char* rest) {
return;
}
if (n >= sizeof(tmp)) {
- dbg_errf(s, "usage: :language c|toy|asm|wat|wasm");
+ dbg_errf(s, "usage: :language c|asm|wat|wasm");
return;
}
driver_memcpy(tmp, p, n);
@@ -2501,7 +2507,7 @@ static void dbg_cmd_help(void) {
"Commands (abbrev. shown):\n"
" h, help show this help\n"
" q, quit exit (Ctrl-D also works)\n"
- " :language c|toy|asm|wasm/wat\n"
+ " :language c|asm|wasm/wat\n"
" select language for jit/expr input\n"
" r, run start fresh execution at entry\n"
" c, cont continue after a stop\n"
@@ -3002,7 +3008,7 @@ static void dbg_complete_info(DriverLineCompletionList* out, const char* prefix,
static void dbg_complete_languages(DriverLineCompletionList* out,
const char* prefix, size_t prefix_len) {
- static const char* const langs[] = {"c", "toy", "asm", "wasm", "wat"};
+ static const char* const langs[] = {"c", "asm", "wasm", "wat"};
size_t i;
for (i = 0; i < sizeof(langs) / sizeof(langs[0]); ++i)
dbg_completion_add_cstr(out, prefix, prefix_len, langs[i]);
diff --git a/driver/cmd/emu.c b/driver/cmd/emu.c
@@ -73,7 +73,8 @@ void driver_help_emu(void) {
" to the guest executable path.\n"
"\n"
"OPTIONS\n"
- " -O0 -O1 -O2 Translator optimization level (default -O0)\n"
+ " -O0 -O1 -O2 Translator optimization level; -O2 aliases "
+ "-O1 (default -O0)\n"
" -interp Run translated blocks through the IR "
"interpreter\n"
" instead of JITing them (forces -O1)\n"
@@ -150,7 +151,7 @@ static int emu_parse(int argc, char** argv, EmuOptions* o) {
continue;
}
if (driver_streq(a, "-O2")) {
- o->opt_level = 2;
+ o->opt_level = 1;
continue;
}
diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c
@@ -134,7 +134,7 @@ void driver_help_ld(void) {
driver_printf(
"%.*s",
KIT_SLICE_ARG(KIT_SLICE_LIT(
- "kit ld — link objects/archives into an executable or shared "
+ "kit ld — link objects/archives into an executable or ELF shared "
"library\n"
"\n"
"USAGE\n"
@@ -145,16 +145,14 @@ void driver_help_ld(void) {
"DESCRIPTION\n"
" Loads each input via host file I/O, optionally parses a -T "
"linker\n"
- " script, and emits an executable (default) or shared library\n"
+ " script, and emits an executable (default) or ELF shared library\n"
" (-shared). The full link surface is exposed: per-archive flags,\n"
" cyclic-resolution groups, build-id, soname/rpath/exports.\n"
"\n"
"OUTPUT\n"
" -o PATH Output path (required, exactly one)\n"
- " -shared Emit a position-independent shared "
- "library "
- "/\n"
- " dylib instead of an executable\n"
+ " -shared Emit a position-independent ELF shared\n"
+ " library instead of an executable\n"
" -r, --relocatable Emit a relocatable partial-link object\n"
"\n"
"ENTRY / SCRIPT\n"
@@ -2665,6 +2663,11 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
"-nostartfiles");
return 1;
}
+ if (o->shared && o->target.obj != KIT_OBJ_ELF) {
+ driver_errf(LD_TOOL,
+ "-shared output is supported only for ELF targets in v1");
+ return 1;
+ }
if (o->relocatable) {
if (o->shared) {
driver_errf(LD_TOOL, "-r and -shared are incompatible");
diff --git a/driver/cmd/run.c b/driver/cmd/run.c
@@ -20,7 +20,7 @@
* .o objects, .a archives) and target/diagnostic flag surface; libkit's
* JIT path forces PIC regardless of `-fPIC`/`-fPIE`/`-mcmodel`.
*
- * Native host-symbol fallback (so JITed C/Toy/object code can call libc) goes
+ * Native host-symbol fallback (so JITed C/object code can call libc) goes
* through driver_dlsym_resolver in driver/env.c. Wasm source inputs do not get
* that fallback; host access is controlled by the Wasm import policy. The
* driver returns whatever the entry returns, or 1 on a compile/link/lookup
@@ -178,7 +178,8 @@ void driver_help_run(void) {
" are accepted but have no observable effect.\n"
"\n"
"COMPILE OPTIONS\n"
- " -O0 -O1 -O2 Optimization level (default -O0)\n"
+ " -O0 -O1 -O2 Optimization level; -O2 aliases -O1 "
+ "(default -O0)\n"
" --no-jit Execute the entry through the IR interpreter\n"
" instead of JIT-compiled native code (forces "
"-O1\n"
@@ -226,7 +227,8 @@ void driver_help_run(void) {
" Import resolver policy (default deny). `test` "
"binds\n"
" only the in-tree env.host_add test shim.\n"
- " `wasi` binds the configured WASI Preview1 "
+ " `wasi` binds the configured partial WASI "
+ "Preview1 "
"shim.\n"
" --wasm-wasi Alias for --wasm-imports=wasi\n"
" --wasm-env=none|allowlist|inherit\n"
@@ -504,7 +506,7 @@ static int run_parse(int argc, char** argv, RunOptions* o) {
continue;
}
if (driver_streq(a, "-O2")) {
- o->opt_level = 2;
+ o->opt_level = 1;
continue;
}
diff --git a/driver/driver.h b/driver/driver.h
@@ -176,6 +176,7 @@ typedef enum DriverToolGroup {
unsigned driver_tool_count(void);
const char* driver_tool_name(unsigned index); /* NULL if out of range */
unsigned driver_tool_groups(unsigned index); /* 0 if out of range */
+int driver_tool_public(unsigned index); /* listed in help/install --all */
/* Index of the tool named `name`, or -1 if there is no such tool. */
int driver_tool_find(const char* name);
@@ -193,13 +194,14 @@ int driver_argv_wants_help(int argc, char** argv, int accept_short_h);
/* Parse a target triple string (`<arch>[-<vendor>]-<os>[-<env>]`) into a
* KitTargetSpec. Recognized arches: x86_64/amd64, i386/i486/i586/i686, aarch64/
- * arm64, arm/armv7, riscv64, riscv32, wasm32, wasm64. Recognized OSes (scanned
- * across the remaining components, so vendor tokens like `pc`/`apple`/
- * `unknown` are skipped): linux, android, darwin/macos, ios/iphoneos,
- * iphonesimulator/ios-simulator, windows/win32, wasi, none/freestanding. Sets
- * arch/os/obj/ptr_size/ptr_align/big_endian; pic and code_model are left at
- * their defaults. Returns 0 on success, nonzero on unrecognized arch or NULL
- * inputs. */
+ * arm64, arm/armv7, riscv64, riscv32, wasm32, wasm64. `wasm64` is recognized as
+ * a reserved spelling, but compiler target construction rejects it in v1.
+ * Recognized OSes (scanned across the remaining components, so vendor tokens
+ * like `pc`/`apple`/`unknown` are skipped): linux, android, darwin/macos,
+ * ios/iphoneos, iphonesimulator/ios-simulator, windows/win32, wasi,
+ * none/freestanding. Sets arch/os/obj/ptr_size/ptr_align/big_endian; pic and
+ * code_model are left at their defaults. Returns 0 on success, nonzero on
+ * unrecognized arch or NULL inputs. */
int driver_target_from_triple(const char* triple, KitTargetSpec* out);
/* Render a canonical driver target triple into `buf`. Returns 0 on success,
@@ -208,10 +210,11 @@ int driver_target_to_triple(KitTargetSpec target, char* buf, size_t cap);
/* Map an architecture-name literal (the arch component of a triple:
* x86_64/amd64, i386/i486/i586/i686, aarch64/arm64, arm/armv7, riscv64,
- * riscv32, wasm32, wasm64) to its KitArchKind and natural pointer size. The
- * single authority for arch-name spellings; driver_target_from_triple uses
- * the same table. Returns 0 on success, nonzero on an unrecognized name or a
- * NULL argument. Out-pointers may be NULL. */
+ * riscv32, wasm32, wasm64) to its KitArchKind and natural pointer size.
+ * `wasm64` is reserved but unsupported for v1 code generation. The single
+ * authority for arch-name spellings; driver_target_from_triple uses the same
+ * table. Returns 0 on success, nonzero on an unrecognized name or a NULL
+ * argument. Out-pointers may be NULL. */
int driver_arch_from_name(const char* name, KitArchKind* arch_out,
uint8_t* ptr_size_out);
diff --git a/driver/env/posix.c b/driver/env/posix.c
@@ -1663,7 +1663,7 @@ static KitArchKind host_arch_self(void) {
}
KitTargetSpec driver_host_target(void) {
- KitTargetSpec t;
+ KitTargetSpec t = {0};
t.arch = host_arch_self();
os_host_target_fill(&t);
t.ptr_size = (uint8_t)sizeof(void*);
diff --git a/driver/env/windows.c b/driver/env/windows.c
@@ -2309,7 +2309,7 @@ static KitArchKind host_arch_self_win(void) {
}
KitTargetSpec driver_host_target(void) {
- KitTargetSpec t;
+ KitTargetSpec t = {0};
t.arch = host_arch_self_win();
t.os = KIT_OS_WINDOWS;
t.obj = KIT_OBJ_COFF;
diff --git a/driver/lib/install_links.c b/driver/lib/install_links.c
@@ -80,6 +80,7 @@ unsigned driver_install_links(DriverEnv* env, const char* bindir,
unsigned done_count = 0, failures = 0;
for (j = 0; j < n; ++j) {
+ if (group_mask == DRIVER_GROUP_ALL && !driver_tool_public(j)) continue;
if (group_mask != DRIVER_GROUP_ALL &&
(driver_tool_groups(j) & group_mask) == 0)
continue;
diff --git a/driver/main.c b/driver/main.c
@@ -119,9 +119,7 @@ static const DriverToolDesc driver_tools[] = {
DRIVER_GROUP_OTHER},
#endif
#if KIT_TOOL_EMU_ENABLED
- {"emu", driver_emu, NULL, driver_help_emu,
- "Run a guest user-mode ELF (aarch64/riscv64) on the host",
- DRIVER_GROUP_OTHER},
+ {"emu", driver_emu, NULL, driver_help_emu, NULL, DRIVER_GROUP_OTHER},
#endif
#if KIT_TOOL_NM_ENABLED
{"nm", driver_nm, NULL, driver_help_nm, "List symbols from object files",
@@ -223,6 +221,10 @@ unsigned driver_tool_groups(unsigned index) {
return driver_tools[index].groups;
}
+int driver_tool_public(unsigned index) {
+ return index < driver_tool_count() && driver_tools[index].summary != NULL;
+}
+
int driver_tool_find(const char* name) {
unsigned i;
if (!name) return -1;
@@ -323,6 +325,7 @@ void driver_help_top(void) {
"\n"
"TOOLS\n");
for (i = 0; i < driver_tool_count(); ++i) {
+ if (!driver_tool_public(i)) continue;
driver_printf(" %-9s %s\n", driver_tools[i].name, driver_tools[i].summary);
}
driver_printf(
diff --git a/include/kit/core.h b/include/kit/core.h
@@ -274,7 +274,7 @@ typedef enum KitAutoVarInit {
} KitAutoVarInit;
typedef struct KitCodeOptions {
- int opt_level; /* 0 direct; 1+ require KIT_OPT_ENABLED */
+ int opt_level; /* 0 direct; 1 optimized; 2 accepted as an alias for 1 */
bool debug_info; /* emit source/debug records when supported */
/* Run the frontend and CG validation path without emitting target code.
* Drivers use this for syntax/semantic checking modes. */
diff --git a/include/kit/target.h b/include/kit/target.h
@@ -30,7 +30,8 @@
* riscv64 / rv64 / riscv64<isa> -> KIT_ARCH_RV64 (8)
* riscv32 / rv32 / riscv32<isa> -> KIT_ARCH_RV32 (4)
* wasm32 -> KIT_ARCH_WASM (4)
- * wasm64 -> KIT_ARCH_WASM (8)
+ * wasm64 -> KIT_ARCH_WASM (8; reserved,
+ * unsupported in v1)
* The single authority for arch-name spellings; kit_target_from_triple() uses
* the same table. Returns true on a hit (writing through non-NULL out
* pointers), false on an unrecognized name or a NULL `name`. Out pointers may
@@ -51,8 +52,9 @@ KIT_API bool kit_arch_from_name(const char* name, KitArchKind* arch_out,
* Sets arch/os/obj/ptr_size/ptr_align/big_endian/os_version_major, and derives
* pic via kit_target_default_pic; code_model is left at KIT_CM_DEFAULT. The
* resolved data-model fields (long_size, wchar_size, ...) are NOT filled in
- * here — they come from kit_target_new(). Returns true on success, false on an
- * unrecognized arch or a NULL `triple`/`out`. */
+ * here — they come from kit_target_new(). `wasm64` is parseable as a reserved
+ * spelling but kit_target_new() rejects it in v1. Returns true on success,
+ * false on an unrecognized arch or a NULL `triple`/`out`. */
KIT_API bool kit_target_from_triple(const char* triple, KitTargetSpec* out);
/* Render a canonical target triple for `spec` into `buf` (e.g.
diff --git a/mk/flags.mk b/mk/flags.mk
@@ -46,11 +46,8 @@ endif
# Auto-initialize automatic (stack) variables to zero. Hardening that defines
# the otherwise-UB of reading an uninitialized local — the same default the
# Linux kernel / Android / Chrome ship; =zero is deterministic and effectively
-# free at our opt levels. It also neutralizes the latent layout-sensitive
-# uninit-read in the kit-run pipeline documented in doc/plan/TODO.md (that
-# entry stays open until MSan pinpoints and fixes the actual read). Probed so a
-# compiler without the flag — notably $(CC)=kit cc in bootstrap stages — simply
-# omits it instead of erroring.
+# free at our opt levels. Probed so a compiler without the flag — notably
+# $(CC)=kit cc in bootstrap stages — simply omits it instead of erroring.
AUTO_INIT_CFLAGS = $(shell $(CC) -ftrivial-auto-var-init=zero -x c -E /dev/null >/dev/null 2>&1 && printf '%s' -ftrivial-auto-var-init=zero)
CFLAGS_COMMON = $(HOST_OPTFLAGS) $(HOST_MODE_CPPFLAGS) $(HOST_MODE_CFLAGS) \
diff --git a/src/api/core.c b/src/api/core.c
@@ -45,6 +45,11 @@ KitStatus kit_target_new(const KitContext* ctx, const KitTargetOptions* opts,
(unsigned)opts->spec.arch);
return KIT_UNSUPPORTED;
}
+ if (opts->spec.arch == KIT_ARCH_WASM && opts->spec.ptr_size != 4u) {
+ kit_ctx_diagf(ctx,
+ "wasm64 is not supported in v1; use a wasm32 target");
+ return KIT_UNSUPPORTED;
+ }
h = ctx->heap;
t = (KitTarget*)h->alloc(h, sizeof(*t), _Alignof(KitTarget));
diff --git a/src/arch/wasm/arch.c b/src/arch/wasm/arch.c
@@ -48,7 +48,7 @@ static KitStatus wasm_target_feature_apply_isa(const Target* target,
(void)target;
(void)words;
(void)nwords;
- if (kit_slice_eq_cstr(isa, "wasm32") || kit_slice_eq_cstr(isa, "wasm64"))
+ if (kit_slice_eq_cstr(isa, "wasm32"))
return KIT_OK;
return KIT_UNSUPPORTED;
}
diff --git a/src/cg/cgir.h b/src/cg/cgir.h
@@ -494,7 +494,7 @@ typedef struct CGSwitchDesc {
const CGSwitchCase* cases;
u32 ncases;
u8 hint; /* KitCgSwitchHint */
- u8 opt_level; /* 0/1/2; reads policy in cg_lower_switch_default */
+ u8 opt_level; /* 0 direct, 1 optimized; public 2 is normalized to 1 */
u8 pad[2];
} CGSwitchDesc;
diff --git a/src/cg/control.c b/src/cg/control.c
@@ -226,7 +226,6 @@ static CGSwitchPlan cg_plan_switch(KitCg* g, const CGSwitchDesc* d) {
}
/* TARGET_DEFAULT: O0 keeps the chain; O1+ runs the density check. */
if (d->opt_level == 0) return plan;
- if (d->opt_level >= 2 && plan.span != d->ncases) return plan;
if (d->ncases < CG_SWITCH_TABLE_MIN_CASES_O1) return plan;
if (plan.span > CG_SWITCH_TABLE_MAX_SPAN_O1) return plan;
if (plan.span > (u64)d->ncases * CG_SWITCH_TABLE_DENSITY_RECIP_O1)
diff --git a/src/cg/session.c b/src/cg/session.c
@@ -126,6 +126,7 @@ KitStatus kit_cg_begin(KitCg* g, KitObjBuilder* out,
compiler_panic((Compiler*)c, api_no_loc(),
"KitCg: unsupported opt_level %d", opt_level);
}
+ if (opt_level > 1) opt_level = 1;
if (opts && opts->emit_ir && opt_level < 1) {
compiler_panic((Compiler*)c, api_no_loc(),
"KitCg: emit_ir requires opt_level >= 1 "
diff --git a/test/api/cg_fp_cmp_test.c b/test/api/cg_fp_cmp_test.c
@@ -245,6 +245,11 @@ static void run_emit(KitArchKind arch, KitOSKind os, KitObjFmt fmt,
int i;
char nm[40];
+ if (arch == KIT_ARCH_WASM) {
+ tgt.ptr_size = 4;
+ tgt.ptr_align = 4;
+ }
+
if (kit_unit_compiler_new(&g_u, tgt, &c) != KIT_OK || !c) {
EXPECT(0, "%s/O%d: compiler_new failed", tag, opt_level);
return;
diff --git a/test/api/target_test.c b/test/api/target_test.c
@@ -212,6 +212,7 @@ static void check_rv64_isa_and_overrides(void) {
static void check_wasm_features(void) {
KitTargetSpec spec = target_spec(KIT_ARCH_WASM, KIT_OS_WASI, KIT_OBJ_WASM);
+ KitTargetSpec parsed;
KitTargetFeature disable_tail_calls[] = {
{KIT_SLICE_LIT("tail-calls"), false},
};
@@ -229,6 +230,26 @@ static void check_wasm_features(void) {
EXPECT(has(t, "bulk-memory"), "wasm default still has bulk-memory");
EXPECT(!has(t, "not-a-feature"), "unknown queried feature is absent");
kit_target_free(t);
+
+ memset(&parsed, 0, sizeof parsed);
+ EXPECT(kit_target_from_triple("wasm64-wasi", &parsed),
+ "wasm64 triple remains parseable as a reserved spelling");
+ EXPECT(parsed.arch == KIT_ARCH_WASM && parsed.ptr_size == 8,
+ "wasm64 triple carries 8-byte pointer size");
+ g_u.last_diag[0] = '\0';
+ EXPECT(make_target(parsed, KIT_SLICE_NULL, NULL, 0, &t) == KIT_UNSUPPORTED,
+ "wasm64 target construction rejected");
+ EXPECT(t == NULL, "wasm64 rejected target leaves target NULL");
+ EXPECT(strstr(g_u.last_diag, "wasm64 is not supported") != NULL,
+ "wasm64 unsupported diagnostic");
+
+ g_u.last_diag[0] = '\0';
+ EXPECT(make_target(spec, KIT_SLICE_LIT("wasm64"), NULL, 0, &t) ==
+ KIT_INVALID,
+ "wasm64 ISA spelling rejected");
+ EXPECT(t == NULL, "wasm64 ISA rejected target leaves target NULL");
+ EXPECT(strstr(g_u.last_diag, "unsupported ISA/profile") != NULL,
+ "wasm64 ISA diagnostic");
}
static void check_errors(void) {
diff --git a/test/arch/aa64_inline_test.c b/test/arch/aa64_inline_test.c
@@ -1,6 +1,7 @@
/* Public-API unit test for the aarch64 inline-asm backend. */
#include <stdint.h>
+#include <string.h>
#include "inline_public_test.h"
@@ -48,6 +49,49 @@ static void aa64_bad_ldr_zr(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
it_inline_asm(c, cg, "ldr w0, [xzr]", NULL, 0, NULL, 0, NULL, 0);
}
+static void aa64_push_local_inputs(KitCompiler* c, KitCg* cg, KitCgTypeId ty,
+ const char* constraint,
+ KitCgAsmOperand* ins, uint32_t n) {
+ KitCgLocal locals[24];
+ KitCgLocalAttrs attrs;
+ KitCgMemAccess mem;
+ memset(&attrs, 0, sizeof attrs);
+ memset(&mem, 0, sizeof mem);
+ mem.type = ty;
+ mem.align = kit_cg_type_align(c, ty);
+ for (uint32_t i = 0; i < n; ++i) {
+ locals[i] = kit_cg_local(cg, ty, attrs);
+ if (ty == kit_cg_type_builtin(c, KIT_CG_BUILTIN_F64))
+ kit_cg_push_float(cg, (double)i + 0.5, ty);
+ else
+ kit_cg_push_int(cg, i + 1u, ty);
+ kit_cg_local_write(cg, locals[i], mem);
+ }
+ for (uint32_t i = 0; i < n; ++i) {
+ kit_cg_local_read(cg, locals[i], mem);
+ ins[i] = it_asm_op(c, constraint, NULL, ty, KIT_CG_ASM_IN);
+ }
+}
+
+static void aa64_optimized_stage(KitCompiler* c, KitCg* cg,
+ KitCgTypeId i64_ty) {
+ KitCgTypeId f64_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_F64);
+ KitCgAsmOperand ins[24];
+ KitCgAsmOperand outs[6];
+
+ aa64_push_local_inputs(c, cg, i64_ty, "r", ins, 20);
+ it_inline_asm(c, cg, "", NULL, 0, ins, 20, NULL, 0);
+
+ aa64_push_local_inputs(c, cg, f64_ty, "w", ins, 24);
+ it_inline_asm(c, cg, "", NULL, 0, ins, 24, NULL, 0);
+
+ for (uint32_t i = 0; i < 6u; ++i)
+ outs[i] = it_asm_op(c, i & 1u ? "=w" : "=r", NULL,
+ i & 1u ? f64_ty : i64_ty, KIT_CG_ASM_OUT);
+ it_inline_asm(c, cg, "", outs, 6, NULL, 0, NULL, 0);
+ for (uint32_t i = 0; i < 6u; ++i) kit_cg_drop(cg);
+}
+
int main(void) {
InlineTestEnv env;
InlineText text;
@@ -91,6 +135,15 @@ int main(void) {
aa64_bad_ldr_zr, "zero register"),
"expected ldr w0, [xzr] to panic");
+ {
+ InlineText opt;
+ IT_EXPECT(&env,
+ it_emit_text_opt(&env, KIT_ARCH_ARM_64, "aa64_optimized_stage",
+ aa64_optimized_stage, 1, &opt),
+ "failed to emit optimized aa64 inline-asm staging case");
+ it_text_close(&opt);
+ }
+
if (env.fails) {
fprintf(stderr, "%d failure(s)\n", env.fails);
return 1;
diff --git a/test/arch/inline_public_test.h b/test/arch/inline_public_test.h
@@ -37,6 +37,7 @@ typedef struct InlineEmit {
KitObjBuilder* ob;
InlineBodyFn body;
const char* name;
+ int opt_level;
} InlineEmit;
static inline KitTargetSpec it_target(KitArchKind arch) {
@@ -61,6 +62,7 @@ static inline KitStatus it_emit_func(KitCompiler* c, void* user) {
if (kit_obj_builder_new(c, &emit->ob) != KIT_OK) return KIT_ERR;
if (kit_cg_new(c, &cg) != KIT_OK || !cg) return KIT_ERR;
memset(&opts, 0, sizeof opts);
+ opts.opt_level = emit->opt_level;
if (kit_cg_begin(cg, emit->ob, &opts) != KIT_OK) return KIT_ERR;
memset(&sig, 0, sizeof sig);
@@ -89,9 +91,9 @@ static inline KitStatus it_emit_func(KitCompiler* c, void* user) {
return KIT_OK;
}
-static inline int it_emit_text(InlineTestEnv* env, KitArchKind arch,
- const char* name, InlineBodyFn body,
- InlineText* text) {
+static inline int it_emit_text_opt(InlineTestEnv* env, KitArchKind arch,
+ const char* name, InlineBodyFn body,
+ int opt_level, InlineText* text) {
KitCompiler* c = NULL;
KitTarget* target = NULL;
KitTargetOptions target_opts;
@@ -106,6 +108,7 @@ static inline int it_emit_text(InlineTestEnv* env, KitArchKind arch,
emit.env = env;
emit.body = body;
emit.name = name;
+ emit.opt_level = opt_level;
memset(&target_opts, 0, sizeof target_opts);
target_opts.spec = it_target(arch);
@@ -179,6 +182,12 @@ done:
return ok;
}
+static inline int it_emit_text(InlineTestEnv* env, KitArchKind arch,
+ const char* name, InlineBodyFn body,
+ InlineText* text) {
+ return it_emit_text_opt(env, arch, name, body, 0, text);
+}
+
static inline void it_text_close(InlineText* text) {
if (text->file) kit_obj_free(text->file);
if (text->writer) kit_writer_close(text->writer);
diff --git a/test/arch/x64_inline_test.c b/test/arch/x64_inline_test.c
@@ -1,6 +1,7 @@
/* Public-API unit test for the x86_64 inline-asm backend. */
#include <stdint.h>
+#include <string.h>
#include "inline_public_test.h"
@@ -34,6 +35,51 @@ static void x64_rax_pin(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
it_inline_asm(c, cg, "syscall", NULL, 0, &in, 1, clob, 3);
}
+static void x64_push_local_inputs(KitCompiler* c, KitCg* cg, KitCgTypeId ty,
+ const char* constraint,
+ KitCgAsmOperand* ins, uint32_t n) {
+ KitCgLocal locals[18];
+ KitCgLocalAttrs attrs;
+ KitCgMemAccess mem;
+ memset(&attrs, 0, sizeof attrs);
+ memset(&mem, 0, sizeof mem);
+ mem.type = ty;
+ mem.align = kit_cg_type_align(c, ty);
+ for (uint32_t i = 0; i < n; ++i) {
+ locals[i] = kit_cg_local(cg, ty, attrs);
+ if (ty == kit_cg_type_builtin(c, KIT_CG_BUILTIN_F64))
+ kit_cg_push_float(cg, (double)i + 0.5, ty);
+ else
+ kit_cg_push_int(cg, i + 1u, ty);
+ kit_cg_local_write(cg, locals[i], mem);
+ }
+ for (uint32_t i = 0; i < n; ++i) {
+ kit_cg_local_read(cg, locals[i], mem);
+ ins[i] = it_asm_op(c, constraint, NULL, ty, KIT_CG_ASM_IN);
+ }
+}
+
+static void x64_optimized_stage(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
+ KitCgTypeId f64_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_F64);
+ KitCgAsmOperand ins[18];
+ KitCgAsmOperand outs[6];
+
+ x64_push_local_inputs(c, cg, i64_ty, "r", ins, 14);
+ it_inline_asm(c, cg, "", NULL, 0, ins, 14, NULL, 0);
+
+ x64_push_local_inputs(c, cg, i64_ty, "q", ins, 14);
+ it_inline_asm(c, cg, "", NULL, 0, ins, 14, NULL, 0);
+
+ x64_push_local_inputs(c, cg, f64_ty, "x", ins, 18);
+ it_inline_asm(c, cg, "", NULL, 0, ins, 18, NULL, 0);
+
+ for (uint32_t i = 0; i < 6u; ++i)
+ outs[i] = it_asm_op(c, i & 1u ? "=x" : "=r", NULL,
+ i & 1u ? f64_ty : i64_ty, KIT_CG_ASM_OUT);
+ it_inline_asm(c, cg, "", outs, 6, NULL, 0, NULL, 0);
+ for (uint32_t i = 0; i < 6u; ++i) kit_cg_drop(cg);
+}
+
int main(void) {
static const uint8_t nops[] = {0x90u, 0x90u};
static const uint8_t movq_rcx_rax[] = {0x48u, 0x89u, 0xc8u};
@@ -79,6 +125,15 @@ int main(void) {
it_text_close(&sc);
}
+ {
+ InlineText opt;
+ IT_EXPECT(&env,
+ it_emit_text_opt(&env, KIT_ARCH_X86_64, "x64_optimized_stage",
+ x64_optimized_stage, 1, &opt),
+ "failed to emit optimized x64 inline-asm staging case");
+ it_text_close(&opt);
+ }
+
if (env.fails) {
fprintf(stderr, "%d failure(s)\n", env.fails);
return 1;
diff --git a/test/dbg/cases/repl-help/expected b/test/dbg/cases/repl-help/expected
@@ -2,7 +2,7 @@ kit dbg — 'h' for help, 'q' to quit
Commands (abbrev. shown):
h, help show this help
q, quit exit (Ctrl-D also works)
- :language c|toy|asm|wasm/wat
+ :language c|asm|wasm/wat
select language for jit/expr input
r, run start fresh execution at entry
c, cont continue after a stop
diff --git a/test/driver/run.sh b/test/driver/run.sh
@@ -217,6 +217,108 @@ else
not_ok "cc-dumpmachine-probe" "$work/cc-dumpmachine.err"
fi
+# ---- shared-library creation is ELF-only for v1 ----
+if "$KIT" cc -target x86_64-macos -shared -nostdlib "$work/main.c" \
+ -o "$work/libbad.dylib" > "$work/cc-shared-macho.out" \
+ 2> "$work/cc-shared-macho.err"; then
+ echo "expected non-ELF -shared to fail" > "$work/cc-shared-macho.diag"
+ not_ok "cc-rejects-nonelf-shared" "$work/cc-shared-macho.diag"
+elif grep -q "only for ELF" "$work/cc-shared-macho.err"; then
+ ok "cc-rejects-nonelf-shared"
+else
+ not_ok "cc-rejects-nonelf-shared" "$work/cc-shared-macho.err"
+fi
+
+if "$KIT" cc -target x86_64-macos -c "$work/main.c" \
+ -o "$work/main-macho.o" > "$work/main-macho.out" \
+ 2> "$work/main-macho.err"; then
+ if "$KIT" ld -target x86_64-macos -shared "$work/main-macho.o" \
+ -o "$work/libbad2.dylib" > "$work/ld-shared-macho.out" \
+ 2> "$work/ld-shared-macho.err"; then
+ echo "expected non-ELF ld -shared to fail" \
+ > "$work/ld-shared-macho.diag"
+ not_ok "ld-rejects-nonelf-shared" "$work/ld-shared-macho.diag"
+ elif grep -q "only for ELF" "$work/ld-shared-macho.err"; then
+ ok "ld-rejects-nonelf-shared"
+ else
+ not_ok "ld-rejects-nonelf-shared" "$work/ld-shared-macho.err"
+ fi
+else
+ not_ok "ld-rejects-nonelf-shared" "$work/main-macho.err"
+fi
+
+if "$KIT" build-lib -target x86_64-macos -dynamic "$work/main.c" \
+ -o "$work/libbad3.dylib" > "$work/buildlib-shared-macho.out" \
+ 2> "$work/buildlib-shared-macho.err"; then
+ echo "expected non-ELF build-lib -dynamic to fail" \
+ > "$work/buildlib-shared-macho.diag"
+ not_ok "build-lib-rejects-nonelf-shared" \
+ "$work/buildlib-shared-macho.diag"
+elif grep -q "only for ELF" "$work/buildlib-shared-macho.err"; then
+ ok "build-lib-rejects-nonelf-shared"
+else
+ not_ok "build-lib-rejects-nonelf-shared" \
+ "$work/buildlib-shared-macho.err"
+fi
+
+# ---- WebAssembly v1 boundary: wasm32 source batches only, no wasm64/link ----
+if "$KIT" cc -target wasm64-none -c "$work/main.c" \
+ -o "$work/bad64.wasm" > "$work/cc-wasm64.out" \
+ 2> "$work/cc-wasm64.err"; then
+ echo "expected wasm64 compile to fail" > "$work/cc-wasm64.diag"
+ not_ok "cc-rejects-wasm64" "$work/cc-wasm64.diag"
+elif grep -q "wasm64 is not supported" "$work/cc-wasm64.err"; then
+ ok "cc-rejects-wasm64"
+else
+ not_ok "cc-rejects-wasm64" "$work/cc-wasm64.err"
+fi
+
+if "$KIT" cc -target x86_64-linux -c "$work/main.c" \
+ -o "$work/wasm-boundary-main.o" > "$work/wasm-boundary-cc.out" \
+ 2> "$work/wasm-boundary-cc.err"; then
+ if "$KIT" build-exe -target wasm32-none "$work/wasm-boundary-main.o" \
+ -o "$work/bad-linked.wasm" > "$work/wasm-object-link.out" \
+ 2> "$work/wasm-object-link.err"; then
+ echo "expected wasm32 object-link attempt to fail" \
+ > "$work/wasm-object-link.diag"
+ not_ok "build-exe-rejects-wasm-object-link" \
+ "$work/wasm-object-link.diag"
+ elif grep -q "accepts only source files" "$work/wasm-object-link.err"; then
+ ok "build-exe-rejects-wasm-object-link"
+ else
+ not_ok "build-exe-rejects-wasm-object-link" \
+ "$work/wasm-object-link.err"
+ fi
+
+ if "$KIT" ar rc "$work/libwasm-boundary.a" \
+ "$work/wasm-boundary-main.o" > "$work/wasm-boundary-ar.out" \
+ 2> "$work/wasm-boundary-ar.err"; then
+ if "$KIT" build-exe -target wasm32-none \
+ "$work/libwasm-boundary.a" -o "$work/bad-archive.wasm" \
+ > "$work/wasm-archive-link.out" \
+ 2> "$work/wasm-archive-link.err"; then
+ echo "expected wasm32 archive-link attempt to fail" \
+ > "$work/wasm-archive-link.diag"
+ not_ok "build-exe-rejects-wasm-archive-link" \
+ "$work/wasm-archive-link.diag"
+ elif grep -q "accepts only source files" \
+ "$work/wasm-archive-link.err"; then
+ ok "build-exe-rejects-wasm-archive-link"
+ else
+ not_ok "build-exe-rejects-wasm-archive-link" \
+ "$work/wasm-archive-link.err"
+ fi
+ else
+ not_ok "build-exe-rejects-wasm-archive-link" \
+ "$work/wasm-boundary-ar.err"
+ fi
+else
+ not_ok "build-exe-rejects-wasm-object-link" \
+ "$work/wasm-boundary-cc.err"
+ not_ok "build-exe-rejects-wasm-archive-link" \
+ "$work/wasm-boundary-cc.err"
+fi
+
# ---- cc -print-file-name probe ----
if "$KIT" cc -print-file-name=crt1.o \
> "$work/cc-print-file-name.out" 2> "$work/cc-print-file-name.err" &&
@@ -710,6 +812,21 @@ else
not_ok "run-source-archive-demand" "$work/run-setup.diag"
fi
+cat > "$work/run-fp.c" <<'SRC'
+int main(void) {
+ volatile double a = 3.0;
+ volatile double b = 2.0;
+ double c = (a * b) + (a / b);
+ int scaled = (int)(c * 10.0);
+ return scaled == 75 ? 0 : 1;
+}
+SRC
+if "$KIT" run "$work/run-fp.c" > "$work/run-fp.out" 2> "$work/run-fp.err"; then
+ ok "run-host-fp-uses-hardware-abi"
+else
+ not_ok "run-host-fp-uses-hardware-abi" "$work/run-fp.err"
+fi
+
# ---- run --script: #! shebang interpreter, argv passthrough, implicit -lc ----
# Make a .c file executable with a `#!` line and run it directly. The kernel
# launches the interpreter and appends the script path + the user's args, so
@@ -1254,6 +1371,37 @@ else
not_ok "cc-emit-ir" "$work/ir-emit.err"
fi
+if "$KIT" cc --help > "$work/cc-help.out" 2> "$work/cc-help.err" &&
+ grep -q -- "-O2 aliases -O1" "$work/cc-help.out"; then
+ ok "cc-help-o2-alias"
+else
+ not_ok "cc-help-o2-alias" "$work/cc-help.err"
+fi
+
+if "$KIT" --help > "$work/kit-help.out" 2> "$work/kit-help.err" &&
+ ! grep -q "^ emu[[:space:]]" "$work/kit-help.out"; then
+ ok "kit-help-hides-emu"
+else
+ not_ok "kit-help-hides-emu" "$work/kit-help.out"
+fi
+
+if "$KIT" dbg --help > "$work/dbg-help.out" 2> "$work/dbg-help.err" &&
+ ! grep -q "toy" "$work/dbg-help.out"; then
+ ok "dbg-help-hides-toy"
+else
+ not_ok "dbg-help-hides-toy" "$work/dbg-help.out"
+fi
+
+if "$KIT" cc -O2 --emit=ir -c "$work/ir.c" -o "$work/ir-o2.out" \
+ > "$work/ir-o2-emit.out" 2> "$work/ir-o2-emit.err" &&
+ cmp -s "$work/ir.out" "$work/ir-o2.out"; then
+ ok "cc-o2-aliases-o1-ir"
+else
+ { echo "expected -O2 IR to match -O1"; cat "$work/ir-o2-emit.err"; } \
+ > "$work/ir-o2.diag"
+ not_ok "cc-o2-aliases-o1-ir" "$work/ir-o2.diag"
+fi
+
# --emit=ir without -O1 must be rejected (no IR tape is recorded at -O0).
if "$KIT" cc --emit=ir -c "$work/ir.c" -o "$work/ir-o0.out" \
> "$work/ir-o0.out.log" 2> "$work/ir-o0.err"; then
@@ -1291,6 +1439,12 @@ fi
inst_all="$work/inst-all"
run_ok "install-all" "$KIT" install --all "$inst_all"
assert_file_exists "install-all-has-mc" "$inst_all/mc"
+if [ -e "$inst_all/emu" ]; then
+ echo "emu present in install --all" > "$work/install-all-emu.diag"
+ not_ok "install-all-hides-emu" "$work/install-all-emu.diag"
+else
+ ok "install-all-hides-emu"
+fi
# Existing entries are an error without -f, replaced with it.
run_fail "install-existing-without-force" "$KIT" install "$inst_dir"
diff --git a/test/wasm/run.sh b/test/wasm/run.sh
@@ -153,16 +153,62 @@ static int bind_canned_imports(void *instance) {
typedef struct { unsigned char *data; unsigned long long pages;
unsigned long long max_pages; unsigned int flags; } WasmStartMemoryPrefix;
-#define WASM_START_MEMORY_PREFIX_COUNT 8u
-#define WASM_START_INSTANCE_SIZE (64u * 1024u)
-#define WASM_START_MEMORY_SIZE (16u * 1024u * 1024u)
+typedef struct {
+ unsigned long long offset;
+ unsigned long long min_pages;
+ unsigned long long max_pages;
+ unsigned int flags;
+ unsigned int reserved;
+} WasmStartMemoryLayout;
+extern const unsigned long long __kit_wasm_instance_size;
+extern const unsigned int __kit_wasm_nmemories;
+__attribute__((weak)) const WasmStartMemoryLayout
+ __kit_wasm_memory_layouts[1] = {{0, 0, 0, 0, 0}};
+
+#define WASM_START_PAGE_SIZE (64ull * 1024ull)
+#define WASM_START_MAX_INSTANCE_SIZE (64ull * 1024ull * 1024ull)
+#define WASM_START_MAX_TOTAL_MEMORY_SIZE (1024ull * 1024ull * 1024ull)
+
+static int setup_instance(void **instance_out) {
+ unsigned long long instance_size = __kit_wasm_instance_size ?
+ __kit_wasm_instance_size : 1ull;
+ unsigned long long total_memory = 0;
+ void *instance;
+ if (instance_size > WASM_START_MAX_INSTANCE_SIZE ||
+ instance_size > (unsigned long long)SIZE_MAX)
+ return 0;
+ instance = calloc(1, (size_t)instance_size);
+ if (!instance) return 0;
+ for (unsigned int i = 0; i < __kit_wasm_nmemories; ++i) {
+ const WasmStartMemoryLayout *ml = &__kit_wasm_memory_layouts[i];
+ WasmStartMemoryPrefix *rec;
+ unsigned long long bytes;
+ void *memory = NULL;
+ if (ml->max_pages < ml->min_pages) return 0;
+ if (ml->max_pages > (~0ull / WASM_START_PAGE_SIZE)) return 0;
+ bytes = ml->max_pages * WASM_START_PAGE_SIZE;
+ if (bytes > WASM_START_MAX_TOTAL_MEMORY_SIZE ||
+ total_memory > WASM_START_MAX_TOTAL_MEMORY_SIZE - bytes ||
+ bytes > (unsigned long long)SIZE_MAX)
+ return 0;
+ if (ml->offset > instance_size ||
+ instance_size - ml->offset < sizeof(WasmStartMemoryPrefix))
+ return 0;
+ total_memory += bytes;
+ if (bytes) {
+ memory = calloc(1, (size_t)bytes);
+ if (!memory) return 0;
+ }
+ rec = (WasmStartMemoryPrefix *)((unsigned char *)instance + ml->offset);
+ rec->data = (unsigned char *)memory;
+ }
+ *instance_out = instance;
+ return 1;
+}
+
int main(void) {
- void *instance = calloc(1, WASM_START_INSTANCE_SIZE);
- unsigned char *memory = calloc(1, WASM_START_MEMORY_SIZE);
- if (!instance || !memory) return 1;
- for (unsigned int i = 0; i < WASM_START_MEMORY_PREFIX_COUNT; ++i)
- ((WasmStartMemoryPrefix *)instance)[i].data =
- memory + i * (WASM_START_MEMORY_SIZE / WASM_START_MEMORY_PREFIX_COUNT);
+ void *instance = NULL;
+ if (!setup_instance(&instance)) return 1;
/* After the memory prefix (which can overlap import slots for memory-less
* modules), before init (which consumes the bound slots). */
if (!bind_canned_imports(instance)) return 1;