kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit aa5af123555320e089cbcd32d8678a81f20e2e43
parent 9c0516c2991fc409b2db17a99fda4a76c857b217
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 18 Jun 2026 18:17:11 -0700

Expand tier1 release coverage

Diffstat:
Mdoc/plan/RELEASE.md | 16++++++++--------
Mlang/wasm/host_imports.c | 32++++++++++++++++++++++++++++++--
Mmk/test.mk | 1+
Msrc/api/wasm_host.c | 4+++-
Mtest/driver-wasm/run.sh | 55++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtest/tier1/frontend/README.md | 3++-
Mtest/tier1/frontend/run-kitchen.sh | 8++++++++
Atest/tier1/kitchen/frontend/wasm_emit_helper.c | 3+++
Atest/tier1/kitchen/frontend/wasm_emit_main.c | 5+++++
Atest/tier1/kitchen/objlink/link/dso_dep.c | 9+++++++++
Atest/tier1/kitchen/objlink/link/dso_main.c | 12++++++++++++
Mtest/tier1/objlink/README.md | 4++--
Mtest/tier1/objlink/link.sh | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/tier1/system/README.md | 2++
14 files changed, 208 insertions(+), 15 deletions(-)

diff --git a/doc/plan/RELEASE.md b/doc/plan/RELEASE.md @@ -133,31 +133,31 @@ converted into a non-public unsupported feature. ### Toolchain and linker -- [ ] C frontend, preprocessing, asm, object, archive, linker, runtime selection, +- [x] C frontend, preprocessing, asm, object, archive, linker, runtime selection, and JIT lanes are covered by `make test-tier1`. -- [ ] ELF DSO creation passes targeted tests for supported ELF targets. -- [ ] ELF executable links against DSOs pass targeted tests for supported ELF +- [x] ELF DSO creation passes targeted tests for supported ELF targets. +- [x] ELF executable links against DSOs pass targeted tests for supported ELF targets. - [x] Non-ELF shared-library creation rejects clearly in `cc`, `ld`, and `build-lib`. - [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 +- [x] 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 - [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 +- [x] Wasm/WAT input and `kit run` execution tests pass. +- [x] WASI Preview1 supported imports are listed and tested; unsupported calls fail with feature-naming diagnostics. - [x] Separate-object/static Wasm linking and wasm64 attempts reject clearly. ### Utility and distribution tools -- [ ] `make test-driver-tools` covers `xxd`, `cmp`, byte-tool aliases, `disas`, +- [x] `make test-driver-tools` covers `xxd`, `cmp`, byte-tool aliases, `disas`, and `mc` as applicable. -- [ ] `make test-driver-cpio` validates `cpio`. +- [x] `make test-driver-cpio` validates `cpio`. - [ ] `make test-driver-gram` and `make test-gram` validate `gram`. - [ ] `make test-driver-cas`, `make test-driver-pkg`, and `make test-dist` validate CAS/package distribution. diff --git a/lang/wasm/host_imports.c b/lang/wasm/host_imports.c @@ -12,6 +12,7 @@ #include <stdint.h> #include <string.h> +#include "core/diag.h" #include "runtime_abi.h" /* Raw wasm WasmValType byte encoding (mirrored from src/wasm/wasm.h so this @@ -57,6 +58,28 @@ static int host_imports_map_valtype(uint8_t raw, KitWasmValType* out) { } } +static void host_imports_diag_unresolved(KitCompiler* c, + const KitWasmImportDesc* d) { + const char* module = d && d->module ? d->module : "<unknown>"; + const char* field = d && d->field ? d->field : "<unknown>"; + if (host_imports_streq(module, "wasi_snapshot_preview1")) { + kit_ctx_diagf(c ? c->ctx : NULL, + "unsupported WASI Preview1 import: %s", field); + } else { + kit_ctx_diagf(c ? c->ctx : NULL, + "wasm host import unresolved: %s.%s", module, field); + } +} + +static void host_imports_diag_unsupported_kind(KitCompiler* c, + const KitWasmImportDesc* d, + const char* kind) { + const char* module = d && d->module ? d->module : "<unknown>"; + const char* field = d && d->field ? d->field : "<unknown>"; + kit_ctx_diagf(c ? c->ctx : NULL, + "unsupported wasm host %s import: %s.%s", kind, module, field); +} + /* Translate an nparams/nresults raw-byte type description into the public * KitWasmImportType. Returns 0 on unsupported value-type byte. The * arrays are written into caller-provided storage. */ @@ -129,7 +152,6 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit, uint32_t ntable_descs = 0; uint32_t nglobal_descs = 0; uint32_t nfunc_import_types = 0; - (void)compiler; if (!jit || !inst) return KIT_INVALID; nimports_meta = (const uint32_t*)kit_jit_lookup( @@ -190,6 +212,7 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit, memory_descs = (const KitWasmMemoryImportDesc*)kit_jit_lookup( jit, KIT_SLICE_LIT("__kit_wasm_memory_import_types")); if (!memory_descs) return KIT_MALFORMED; + host_imports_diag_unsupported_kind(compiler, d, "memory"); return KIT_UNSUPPORTED; case KIT_WASM_IMPORT_TABLE: if (d->desc_index >= ntable_descs) return KIT_MALFORMED; @@ -197,6 +220,7 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit, table_descs = (const KitWasmTableImportDesc*)kit_jit_lookup( jit, KIT_SLICE_LIT("__kit_wasm_table_import_types")); if (!table_descs) return KIT_MALFORMED; + host_imports_diag_unsupported_kind(compiler, d, "table"); return KIT_UNSUPPORTED; case KIT_WASM_IMPORT_GLOBAL: if (d->desc_index >= nglobal_descs) return KIT_MALFORMED; @@ -204,6 +228,7 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit, global_descs = (const KitWasmGlobalImportDesc*)kit_jit_lookup( jit, KIT_SLICE_LIT("__kit_wasm_global_import_types")); if (!global_descs) return KIT_MALFORMED; + host_imports_diag_unsupported_kind(compiler, d, "global"); return KIT_UNSUPPORTED; default: return KIT_MALFORMED; @@ -225,7 +250,10 @@ KIT_API KitStatus kit_wasm_bind_host_imports(KitCompiler* compiler, KitJit* jit, return KIT_MALFORMED; fn = resolve(user, d->module, d->field, &type); } - if (!fn) return KIT_NOT_FOUND; + if (!fn) { + host_imports_diag_unresolved(compiler, d); + return KIT_NOT_FOUND; + } /* Slot is a void* at byte offset slot_offset inside the instance struct. * The KitWasmFuncImport record is { void* fn; } so the offset of the * field is also the offset of the fn pointer. */ diff --git a/mk/test.mk b/mk/test.mk @@ -300,6 +300,7 @@ test-t1-emu-interp: test-t1-wasm: bin @$(T1_LOG) wasm env KIT=$(abspath $(BIN)) test/tier1/frontend/run-kitchen.sh wasm + @$(T1_LOG) wasm-driver $(MAKE) --no-print-directory test-driver-wasm test-t1-tools: @$(T1_LOG) tools test/tier1/system/tools.sh diff --git a/src/api/wasm_host.c b/src/api/wasm_host.c @@ -850,6 +850,8 @@ static void* wasm_wasi_resolve(void* user, const char* module, KitWasmValType r_i32[1] = {KIT_WASM_VAL_I32}; KitWasmValType p_proc_exit[1] = {KIT_WASM_VAL_I32}; KitWasmValType p2_i32[2] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32}; + KitWasmValType p3_i32[3] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, + KIT_WASM_VAL_I32}; KitWasmValType p4_i32[4] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, KIT_WASM_VAL_I32}; KitWasmValType p5_i32[5] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, @@ -892,7 +894,7 @@ static void* wasm_wasi_resolve(void* user, const char* module, wasm_sig(type, p2_i32, 2u, r_i32, 1u)) return (void*)(uintptr_t)wasi_fd_prestat_get; if (wasm_streq(field, "fd_prestat_dir_name") && - wasm_sig(type, p4_i32, 4u, r_i32, 1u)) + wasm_sig(type, p3_i32, 3u, r_i32, 1u)) return (void*)(uintptr_t)wasi_fd_prestat_dir_name; if (wasm_streq(field, "path_open") && wasm_sig(type, p_path_open, 9u, r_i32, 1u)) diff --git a/test/driver-wasm/run.sh b/test/driver-wasm/run.sh @@ -159,6 +159,54 @@ cat > "$work/wasi_args.wat" <<'WAT' i32.const 0)) WAT +cat > "$work/wasi_runtime_caps.wat" <<'WAT' +(module + (import "wasi_snapshot_preview1" "random_get" + (func $random_get (param i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "clock_time_get" + (func $clock_time_get (param i32 i64 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_prestat_get" + (func $fd_prestat_get (param i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_prestat_dir_name" + (func $fd_prestat_dir_name (param i32 i32 i32) (result i32))) + (memory 1) + (func (export "test_main") (result i32) + (local $err i32) + i32.const 64 + i32.const 16 + call $random_get + local.set $err + (if (i32.ne (local.get $err) (i32.const 0)) + (then (return (i32.const 41)))) + i32.const 1 + i64.const 0 + i32.const 96 + call $clock_time_get + local.set $err + (if (i32.ne (local.get $err) (i32.const 0)) + (then (return (i32.const 42)))) + i32.const 3 + i32.const 128 + call $fd_prestat_get + local.set $err + (if (i32.ne (local.get $err) (i32.const 0)) + (then (return (i32.const 43)))) + (if (i32.ne (i32.load (i32.const 132)) (i32.const 8)) + (then (return (i32.const 44)))) + i32.const 3 + i32.const 160 + i32.const 8 + call $fd_prestat_dir_name + local.set $err + (if (i32.ne (local.get $err) (i32.const 0)) + (then (return (i32.const 45)))) + (if (i32.ne (i32.load8_u (i32.const 160)) (i32.const 47)) + (then (return (i32.const 46)))) + (if (i32.ne (i32.load8_u offset=1 (i32.const 160)) (i32.const 115)) + (then (return (i32.const 47)))) + i32.const 0)) +WAT + cat > "$work/wasi_fs_read.wat" <<'WAT' (module (import "wasi_snapshot_preview1" "path_open" @@ -447,6 +495,11 @@ run_ok "wasm-wasi-env-set" \ run_ok "wasm-wasi-args" \ "$KIT" run --wasm-wasi -e test_main "$work/wasi_args.wat" -- alpha +run_ok "wasm-wasi-random-clock-prestat" \ + "$KIT" run --wasm-wasi --wasm-clock=monotonic --wasm-random=seed:abcd \ + --wasm-map-dir="$work=/sandbox:ro" -e test_main \ + "$work/wasi_runtime_caps.wat" + run_ok "wasm-wasi-fs-read" \ "$KIT" run --wasm-wasi --wasm-map-dir="$work=/sandbox:ro" \ -e test_main "$work/wasi_fs_read.wat" @@ -476,7 +529,7 @@ run_fail "wasm-wasi-unknown-import-fails" \ "$KIT" run --wasm-wasi -e test_main "$work/wasi_unknown.wat" contains "wasm-wasi-unknown-import-diag" \ "$work/wasm-wasi-unknown-import-fails.err" \ - "wasm host import unresolved" + "path_unlink_file" run_fail "wasm-flags-reject-native" \ "$KIT" run --wasm-memory-max=1M "$work/main.c" diff --git a/test/tier1/frontend/README.md b/test/tier1/frontend/README.md @@ -10,7 +10,8 @@ They are intended as quick sentinels, not replacements for the full suites. - `toy_kitchen.toy`: tuple data, function pointer call, switch, JIT run, and interpreter run. - `wasm_kitchen.wat`: memory, mutable global, loop/control flow, call, and - `kit run` through the Wasm frontend. + `kit run` through the Wasm frontend. The Wasm sentinel also builds a small + C pair to `wasm32-none` with `build-exe` and runs the produced `.wasm`. Run one suite with: diff --git a/test/tier1/frontend/run-kitchen.sh b/test/tier1/frontend/run-kitchen.sh @@ -116,6 +116,7 @@ run_toy() { run_wasm() { local src="$fixture_root/wasm_kitchen.wat" + local wasm="$1/wasm_emit.wasm" local expected local run_rc expected=$(expected_rc "$fixture_root/wasm_kitchen.expected") @@ -124,6 +125,13 @@ run_wasm() { "$KIT" run -e test_main "$src" run_rc=$? check_rc "wasm/kitchen-jit" "$run_rc" "$expected" + + "$KIT" build-exe -target wasm32-none \ + "$fixture_root/wasm_emit_main.c" "$fixture_root/wasm_emit_helper.c" \ + -e test_main -o "$wasm" + "$KIT" run -e test_main "$wasm" + run_rc=$? + check_rc "wasm/compiled-wasm-jit" "$run_rc" 0 } run_suite() { diff --git a/test/tier1/kitchen/frontend/wasm_emit_helper.c b/test/tier1/kitchen/frontend/wasm_emit_helper.c @@ -0,0 +1,3 @@ +int tier1_wasm_add(int a, int b) { + return a + b; +} diff --git a/test/tier1/kitchen/frontend/wasm_emit_main.c b/test/tier1/kitchen/frontend/wasm_emit_main.c @@ -0,0 +1,5 @@ +extern int tier1_wasm_add(int a, int b); + +int test_main(void) { + return tier1_wasm_add(20, 22) == 42 ? 0 : 1; +} diff --git a/test/tier1/kitchen/objlink/link/dso_dep.c b/test/tier1/kitchen/objlink/link/dso_dep.c @@ -0,0 +1,9 @@ +int tier1_dso_data = 7; + +__attribute__((noinline)) int tier1_dso_value(int x) { + return x + tier1_dso_data; +} + +__attribute__((visibility("hidden"))) int tier1_dso_hidden(int x) { + return x * 3; +} diff --git a/test/tier1/kitchen/objlink/link/dso_main.c b/test/tier1/kitchen/objlink/link/dso_main.c @@ -0,0 +1,12 @@ +extern int tier1_dso_data; +extern int tier1_dso_value(int x); + +int tier1_dso_consumer(void) { + return tier1_dso_value(35) == 42 && tier1_dso_data == 7 ? 0 : 1; +} + +void _start(void) { + (void)tier1_dso_consumer(); + for (;;) { + } +} diff --git a/test/tier1/objlink/README.md b/test/tier1/objlink/README.md @@ -11,8 +11,8 @@ Fixtures live under `test/tier1/kitchen/objlink/`: relocations, and per-function/per-data sections; it is round-tripped with `kit objcopy` and compared through the ELF normalizer. - `link/`: a tiny multi-object program linked through `kit ar` and `kit ld` - with an archive member and `--gc-sections`, then inspected as an executable - image. + with an archive member and `--gc-sections`, plus a structural ELF DSO and + executable-against-DSO lane. - `interop/`: a kit-produced object linked by clang/lld when available, and a clang-produced object linked back through kit. diff --git a/test/tier1/objlink/link.sh b/test/tier1/objlink/link.sh @@ -21,6 +21,19 @@ CFLAGS=( --target="$TRIPLE" -O1 -ffreestanding -fno-stack-protector -fno-PIC -fno-pie -fcommon -ffunction-sections -fdata-sections ) +PIC_CFLAGS=( + --target="$TRIPLE" -O1 -ffreestanding -fno-stack-protector + -fPIC -ffunction-sections -fdata-sections +) + +dynamic_linker() { + case "$ARCH" in + aa64|aarch64|arm64) printf '%s\n' /lib/ld-linux-aarch64.so.1 ;; + x64|x86_64|amd64) printf '%s\n' /lib64/ld-linux-x86-64.so.2 ;; + rv64|riscv64) printf '%s\n' /lib/ld-linux-riscv64-lp64d.so.1 ;; + *) printf '%s\n' /lib/ld.so ;; + esac +} echo "tier1 link: compile $ARCH objects" t1_run "$LOG_DIR/link-main-clang.log" \ @@ -57,4 +70,60 @@ t1_assert_not_grep "link/gc-dead-local-function" \ '[[:space:]]\.text\.tier1_dead_local[[:space:]]+PROGBITS' \ "$WORK/tier1.readelf" +echo "tier1 link: ELF DSO creation" +t1_run "$LOG_DIR/link-dso-dep-cc.log" \ + "$KIT" cc -target "$TRIPLE" -O1 -fPIC -ffreestanding -nostdlib \ + -c "$KITCHEN/dso_dep.c" -o "$WORK/dso_dep.o" +KIT_TEST_ARCH="$ARCH" t1_run "$LOG_DIR/link-dso-so.log" \ + env KIT_TEST_ARCH="$ARCH" "$KIT" ld -target "$TRIPLE" \ + -shared -soname libtier1dep.so \ + "$WORK/dso_dep.o" -o "$WORK/libtier1dep.so" + +"$READELF_BIN" -h "$WORK/libtier1dep.so" >"$WORK/libtier1dep.hdr" \ + 2>"$LOG_DIR/link-dso-hdr.log" +"$READELF_BIN" -l "$WORK/libtier1dep.so" >"$WORK/libtier1dep.phdr" \ + 2>"$LOG_DIR/link-dso-phdr.log" +"$READELF_BIN" -d "$WORK/libtier1dep.so" >"$WORK/libtier1dep.dynamic" \ + 2>"$LOG_DIR/link-dso-dynamic.log" +"$READELF_BIN" --dyn-syms "$WORK/libtier1dep.so" >"$WORK/libtier1dep.dynsym" \ + 2>"$LOG_DIR/link-dso-dynsym.log" + +t1_assert_grep "link/dso-type" 'Type:[[:space:]]+DYN' "$WORK/libtier1dep.hdr" +t1_assert_not_grep "link/dso-no-interp" 'INTERP' "$WORK/libtier1dep.phdr" +t1_assert_grep "link/dso-has-dynamic" 'DYNAMIC' "$WORK/libtier1dep.phdr" +t1_assert_grep "link/dso-soname" 'SONAME.*libtier1dep\.so' \ + "$WORK/libtier1dep.dynamic" +t1_assert_grep "link/dso-export-func" \ + 'GLOBAL[[:space:]]+DEFAULT.*tier1_dso_value' "$WORK/libtier1dep.dynsym" +t1_assert_grep "link/dso-export-data" \ + 'GLOBAL[[:space:]]+DEFAULT.*tier1_dso_data' "$WORK/libtier1dep.dynsym" +t1_assert_not_grep "link/dso-hidden-not-exported" \ + 'GLOBAL[[:space:]]+DEFAULT.*tier1_dso_hidden' "$WORK/libtier1dep.dynsym" + +echo "tier1 link: ELF executable against DSO" +t1_run "$LOG_DIR/link-dso-main-cc.log" \ + clang "${PIC_CFLAGS[@]}" -c "$KITCHEN/dso_main.c" -o "$WORK/dso_main.o" +KIT_TEST_ARCH="$ARCH" t1_run "$LOG_DIR/link-dso-exe.log" \ + env KIT_TEST_ARCH="$ARCH" "$KIT" ld -target "$TRIPLE" -pie -e _start \ + -dynamic-linker "$(dynamic_linker)" \ + "$WORK/dso_main.o" "$WORK/libtier1dep.so" \ + -o "$WORK/dso_consumer.exe" + +"$READELF_BIN" -d "$WORK/dso_consumer.exe" >"$WORK/dso_consumer.dynamic" \ + 2>"$LOG_DIR/link-dso-consumer-dynamic.log" +"$READELF_BIN" --dyn-syms "$WORK/dso_consumer.exe" \ + >"$WORK/dso_consumer.dynsym" \ + 2>"$LOG_DIR/link-dso-consumer-dynsym.log" +"$READELF_BIN" -r "$WORK/dso_consumer.exe" >"$WORK/dso_consumer.relocs" \ + 2>"$LOG_DIR/link-dso-consumer-relocs.log" + +t1_assert_grep "link/dso-consumer-needed" 'NEEDED.*libtier1dep\.so' \ + "$WORK/dso_consumer.dynamic" +t1_assert_grep "link/dso-consumer-import-func" \ + 'UND.*tier1_dso_value' "$WORK/dso_consumer.dynsym" +t1_assert_grep "link/dso-consumer-import-data" \ + 'UND.*tier1_dso_data' "$WORK/dso_consumer.dynsym" +t1_assert_grep "link/dso-consumer-dynrelocs" \ + 'JUMP_SLOT|GLOB_DAT|RELATIVE' "$WORK/dso_consumer.relocs" + echo "tier1 link: ok" diff --git a/test/tier1/system/README.md b/test/tier1/system/README.md @@ -25,6 +25,8 @@ tails the captured log. - `dist.sh` runs one CAS/package fixture covering tree add/verify/materialize, tar.gz verification, kpkg create/inspect/verify/unpack, and one tamper rejection. +- `test-t1-wasm` also runs the driver Wasm sandbox fixture, covering the + supported WASI Preview1 import subset and unsupported-import diagnostics. ## Deferred