commit aa5af123555320e089cbcd32d8678a81f20e2e43
parent 9c0516c2991fc409b2db17a99fda4a76c857b217
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 18 Jun 2026 18:17:11 -0700
Expand tier1 release coverage
Diffstat:
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