Skip to content

Commit ebc2bb0

Browse files
committed
zrt CLI runner
1 parent 000255a commit ebc2bb0

4 files changed

Lines changed: 286 additions & 10 deletions

File tree

Makefile

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ IRCHECK_BUILD := $(BUILD)/ircheck
4545
ZASM_BIN_BUILD := $(BUILD)/zasm_bin
4646
ZASM_VERIFY_BUILD := $(BUILD)/zasm_verify
4747
ZASM_RT_BUILD := $(BUILD)/zasm_rt
48+
ZRT_BUILD := $(BUILD)/zrt
4849

4950
UNAME_S := $(shell uname -s)
5051
UNAME_M := $(shell uname -m)
@@ -129,17 +130,18 @@ ZAS_GEN_CFLAGS := $(CFLAGS) -Wno-sign-compare -Wno-unused-function -Wno-unneeded
129130

130131
all: zas zld
131132

132-
build: zas zld ircheck zrun zlnt zop zxc zir zem lower
133+
build: zas zld ircheck zrun zrt zlnt zop zxc zir zem lower
133134

134135
pipeline: build
135136
./scripts/pipeline.sh --skip-build
136137

137-
install: zas zld ircheck zrun zlnt zop zxc zir zem lower
138+
install: zas zld ircheck zrun zrt zlnt zop zxc zir zem lower
138139
@mkdir -p $(DESTDIR)$(BINDIR)
139140
@install -m 0755 $(BIN)/zas $(DESTDIR)$(BINDIR)/zas
140141
@install -m 0755 $(BIN)/zld $(DESTDIR)$(BINDIR)/zld
141142
@install -m 0755 $(BIN)/ircheck $(DESTDIR)$(BINDIR)/ircheck
142143
@install -m 0755 $(BIN)/zrun $(DESTDIR)$(BINDIR)/zrun
144+
@install -m 0755 $(BIN)/zrt $(DESTDIR)$(BINDIR)/zrt
143145
@install -m 0755 $(BIN)/zlnt $(DESTDIR)$(BINDIR)/zlnt
144146
@install -m 0755 $(BIN)/zop $(DESTDIR)$(BINDIR)/zop
145147
@install -m 0755 $(BIN)/zxc $(DESTDIR)$(BINDIR)/zxc
@@ -153,7 +155,7 @@ install-devtools: zasm-bin-wrap
153155
@install -m 0755 tools/zasm_bin_wrap.py $(DESTDIR)$(BINDIR)/zasm-bin-wrap
154156

155157
dirs:
156-
mkdir -p $(BIN) $(ZAS_BUILD) $(ZOP_BUILD) $(ZXC_BUILD) $(ZIR_BUILD) $(ZLD_BUILD) $(IRCHECK_BUILD) $(ZRUN_BUILD) $(ZLNT_BUILD) $(LOWER_BUILD) $(ZEM_BUILD) $(ZEM_BUILD)/exec $(ZASM_BIN_BUILD) $(ZASM_VERIFY_BUILD) $(ZASM_RT_BUILD)
158+
mkdir -p $(BIN) $(ZAS_BUILD) $(ZOP_BUILD) $(ZXC_BUILD) $(ZIR_BUILD) $(ZLD_BUILD) $(IRCHECK_BUILD) $(ZRUN_BUILD) $(ZRT_BUILD) $(ZLNT_BUILD) $(LOWER_BUILD) $(ZEM_BUILD) $(ZEM_BUILD)/exec $(ZASM_BIN_BUILD) $(ZASM_VERIFY_BUILD) $(ZASM_RT_BUILD)
157159

158160
$(VERSION_HEADER): $(VERSION_FILE)
159161
@ver=$$(cat $(VERSION_FILE)); \
@@ -1025,6 +1027,28 @@ test-zasm-rt-smoke: zingcore25 zxc-lib zasm-bin-lib zasm-verify-lib zasm-rt-lib
10251027
$(CC) $(CFLAGS) -Iinclude -I$(ZEM_ZINGCORE25_DIR)/zingcore/include test/zasm_rt_smoke.c $(BIN)/libzasm_rt.a $(BIN)/libzxc.a $(BIN)/libzasm_bin.a $(BIN)/libzasm_verify.a $(ZEM_ZINGCORE25_LIB) -o $(BUILD)/zasm_rt_smoke; \
10261028
$(BUILD)/zasm_rt_smoke
10271029

1030+
# ---- zrt (zasm_rt runner) ----
1031+
1032+
ZRT_SRC := \
1033+
src/zrt/main.c
1034+
1035+
ZRT_OBJ := \
1036+
$(ZRT_BUILD)/main.o
1037+
1038+
$(ZRT_BUILD)/%.o: src/zrt/%.c $(VERSION_HEADER) | dirs
1039+
$(CC) $(CPPFLAGS) $(CFLAGS) -I$(ZEM_ZINGCORE25_DIR)/zingcore/include -Isrc/zrt -c $< -o $@
1040+
1041+
zrt: zingcore25 zxc-lib zasm-bin-lib zasm-verify-lib zasm-rt-lib $(ZRT_OBJ) | dirs
1042+
$(CC) $(CFLAGS) $(ZRT_OBJ) $(BIN)/libzasm_rt.a $(BIN)/libzxc.a $(BIN)/libzasm_bin.a $(BIN)/libzasm_verify.a $(ZEM_ZINGCORE25_LIB) -o $(BIN)/zrt $(LDFLAGS)
1043+
ln -sf $(PLATFORM)/zrt $(BIN_ROOT)/zrt
1044+
1045+
.PHONY: zrt
1046+
1047+
test-zrt-duel: build
1048+
bash test/jit_duel_smoke.sh
1049+
1050+
.PHONY: test-zrt-duel
1051+
10281052
# ---- zcc (IR -> C) ----
10291053

10301054
zcc:

src/zop/main.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,22 @@ static const char* skip_ws(const char* p) {
4646
static const char* find_key_value(const char* line, const char* key) {
4747
char pat[64];
4848
snprintf(pat, sizeof(pat), "\"%s\"", key);
49-
const char* p = strstr(line, pat);
50-
if (!p) return NULL;
51-
p += strlen(pat);
52-
p = skip_ws(p);
53-
if (*p != ':') return NULL;
54-
p++;
55-
return skip_ws(p);
49+
50+
const char* p = line;
51+
while ((p = strstr(p, pat)) != NULL) {
52+
const char* q = p + strlen(pat);
53+
q = skip_ws(q);
54+
if (*q != ':') {
55+
/* This can match string values (e.g. the value "op" in "k":"op").
56+
* Keep searching for an actual key occurrence.
57+
*/
58+
p = p + 1;
59+
continue;
60+
}
61+
q++;
62+
return skip_ws(q);
63+
}
64+
return NULL;
5665
}
5766

5867
static char* parse_json_string(const char** p) {

src/zrt/main.c

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/* SPDX-FileCopyrightText: 2026 Frogfish */
2+
/* SPDX-License-Identifier: GPL-3.0-or-later */
3+
4+
#include <errno.h>
5+
#include <fcntl.h>
6+
#include <stdint.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <string.h>
10+
#include <sys/stat.h>
11+
#include <unistd.h>
12+
13+
#include "version.h"
14+
#include "zasm_rt.h"
15+
#include "zi_hostlib25.h"
16+
17+
extern char **environ;
18+
19+
static void print_help(FILE *out) {
20+
fprintf(out,
21+
"zrt — run a .zasm.bin v2 container via zasm_rt\n"
22+
"\n"
23+
"Usage:\n"
24+
" zrt <program.zasm.bin>\n"
25+
"\n"
26+
"Options:\n"
27+
" --help Show this help message\n"
28+
" --version Show version information\n");
29+
}
30+
31+
static void print_version(void) {
32+
fprintf(stdout, "zrt %s\n", ZASM_VERSION);
33+
}
34+
35+
static int read_whole_file(const char *path, uint8_t **out_buf, size_t *out_len) {
36+
if (!path || !out_buf || !out_len) return 0;
37+
*out_buf = NULL;
38+
*out_len = 0;
39+
40+
int fd = open(path, O_RDONLY);
41+
if (fd < 0) return 0;
42+
43+
struct stat st;
44+
if (fstat(fd, &st) != 0) {
45+
close(fd);
46+
return 0;
47+
}
48+
49+
if (st.st_size < 0) {
50+
close(fd);
51+
return 0;
52+
}
53+
54+
size_t cap = (size_t)st.st_size;
55+
if (cap == 0) {
56+
close(fd);
57+
return 0;
58+
}
59+
60+
uint8_t *buf = (uint8_t *)malloc(cap);
61+
if (!buf) {
62+
close(fd);
63+
return 0;
64+
}
65+
66+
size_t off = 0;
67+
while (off < cap) {
68+
ssize_t n = read(fd, buf + off, cap - off);
69+
if (n == 0) break;
70+
if (n < 0) {
71+
free(buf);
72+
close(fd);
73+
return 0;
74+
}
75+
off += (size_t)n;
76+
}
77+
78+
close(fd);
79+
80+
if (off != cap) {
81+
free(buf);
82+
return 0;
83+
}
84+
85+
*out_buf = buf;
86+
*out_len = cap;
87+
return 1;
88+
}
89+
90+
static void print_diag(const zasm_rt_diag_t *d) {
91+
if (!d) return;
92+
93+
if (d->err == ZASM_RT_ERR_BAD_CONTAINER) {
94+
fprintf(stderr, "diag: container: bin_err=%d off=%u tag=%s\n",
95+
(int)d->bin_err, d->bin_off, d->bin_tag);
96+
}
97+
98+
if (d->err == ZASM_RT_ERR_VERIFY_FAIL) {
99+
fprintf(stderr, "diag: verify: err=%d off=%zu opcode=0x%02x\n",
100+
(int)d->verify_err, d->verify_off, (unsigned)d->verify_opcode);
101+
}
102+
}
103+
104+
int main(int argc, char **argv) {
105+
if (argc < 2) {
106+
print_help(stderr);
107+
return 2;
108+
}
109+
110+
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
111+
print_help(stdout);
112+
return 0;
113+
}
114+
115+
if (strcmp(argv[1], "--version") == 0) {
116+
print_version();
117+
return 0;
118+
}
119+
120+
const char *path = argv[1];
121+
122+
if (!zi_hostlib25_init_all(argc, (const char *const *)argv, (const char *const *)environ)) {
123+
fprintf(stderr, "zrt: error: zi_hostlib25_init_all failed\n");
124+
return 1;
125+
}
126+
127+
uint8_t *file = NULL;
128+
size_t file_len = 0;
129+
if (!read_whole_file(path, &file, &file_len)) {
130+
fprintf(stderr, "zrt: error: failed to read '%s' (%s)\n", path, strerror(errno));
131+
return 1;
132+
}
133+
134+
zasm_rt_engine_t *engine = NULL;
135+
zasm_rt_err_t e = zasm_rt_engine_create(&engine);
136+
if (e != ZASM_RT_OK) {
137+
fprintf(stderr, "zrt: error: engine_create: %s\n", zasm_rt_err_str(e));
138+
free(file);
139+
return 1;
140+
}
141+
142+
zasm_rt_diag_t diag;
143+
zasm_rt_module_t *module = NULL;
144+
e = zasm_rt_module_load_v2(engine, file, file_len, NULL, &module, &diag);
145+
free(file);
146+
if (e != ZASM_RT_OK) {
147+
fprintf(stderr, "zrt: error: module_load_v2: %s\n", zasm_rt_err_str(e));
148+
print_diag(&diag);
149+
zasm_rt_engine_destroy(engine);
150+
return 1;
151+
}
152+
153+
const zi_host_v1 *host = zi_runtime25_host();
154+
zasm_rt_instance_t *inst = NULL;
155+
e = zasm_rt_instance_create(engine, module, NULL, host, &inst, &diag);
156+
if (e != ZASM_RT_OK) {
157+
fprintf(stderr, "zrt: error: instance_create: %s\n", zasm_rt_err_str(e));
158+
print_diag(&diag);
159+
zasm_rt_module_destroy(module);
160+
zasm_rt_engine_destroy(engine);
161+
return 1;
162+
}
163+
164+
e = zasm_rt_instance_run(inst, &diag);
165+
if (e != ZASM_RT_OK) {
166+
fprintf(stderr, "zrt: error: instance_run: %s\n", zasm_rt_err_str(e));
167+
print_diag(&diag);
168+
zasm_rt_instance_destroy(inst);
169+
zasm_rt_module_destroy(module);
170+
zasm_rt_engine_destroy(engine);
171+
return 1;
172+
}
173+
174+
zasm_rt_instance_destroy(inst);
175+
zasm_rt_module_destroy(module);
176+
zasm_rt_engine_destroy(engine);
177+
return 0;
178+
}

test/jit_duel_smoke.sh

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env bash
2+
# SPDX-FileCopyrightText: 2026 Frogfish
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
set -euo pipefail
6+
7+
BIN_DIR="bin"
8+
9+
if [[ ! -x "$BIN_DIR/zas" || ! -x "$BIN_DIR/zld" || ! -x "$BIN_DIR/zrun" || ! -x "$BIN_DIR/zop" || ! -x "$BIN_DIR/zrt" ]]; then
10+
echo "jit_duel_smoke: missing tools in $BIN_DIR (run: make build zrt)" >&2
11+
exit 2
12+
fi
13+
14+
mkdir -p build/jit_duel
15+
16+
base="ret_only"
17+
wat="build/jit_duel/${base}.wat"
18+
zasm="build/jit_duel/${base}.zasm.bin"
19+
20+
ref_out="build/jit_duel/${base}.ref.out"
21+
ref_err="build/jit_duel/${base}.ref.err"
22+
jit_out="build/jit_duel/${base}.jit.out"
23+
jit_err="build/jit_duel/${base}.jit.err"
24+
25+
# Reference runner side: minimal program via the standard pipeline.
26+
printf 'RET\n' | "$BIN_DIR/zas" | "$BIN_DIR/zld" > "$wat"
27+
28+
# JIT side: minimal `.zasm.bin` v2 containing a single RET opcode (0x01).
29+
cat > build/jit_duel/${base}.opcodes.jsonl <<'EOF'
30+
{"ir":"zasm-opcodes-v1","k":"op","op":1,"rd":0,"rs1":0,"rs2":0,"imm12":0}
31+
EOF
32+
"$BIN_DIR/zop" --container build/jit_duel/${base}.opcodes.jsonl > "$zasm"
33+
34+
set +e
35+
"$BIN_DIR/zrun" "$wat" </dev/null >"$ref_out" 2>"$ref_err"
36+
ref_rc=$?
37+
"$BIN_DIR/zrt" "$zasm" </dev/null >"$jit_out" 2>"$jit_err"
38+
jit_rc=$?
39+
set -e
40+
41+
if [[ $ref_rc -ne 0 || $jit_rc -ne 0 ]]; then
42+
echo "jit_duel_smoke: rc mismatch (ref=$ref_rc jit=$jit_rc)" >&2
43+
exit 1
44+
fi
45+
46+
if ! cmp -s "$jit_out" "$ref_out"; then
47+
echo "jit_duel_smoke: stdout mismatch" >&2
48+
echo "ref:" >&2
49+
od -An -tx1 "$ref_out" >&2
50+
echo "jit:" >&2
51+
od -An -tx1 "$jit_out" >&2
52+
exit 1
53+
fi
54+
55+
if ! cmp -s "$jit_err" "$ref_err"; then
56+
echo "jit_duel_smoke: stderr mismatch" >&2
57+
echo "ref:" >&2
58+
od -An -tx1 "$ref_err" >&2
59+
echo "jit:" >&2
60+
od -An -tx1 "$jit_err" >&2
61+
exit 1
62+
fi
63+
64+
echo "jit_duel_smoke: ok: $base"
65+
echo "jit_duel_smoke: all cases passed"

0 commit comments

Comments
 (0)