commit 681d084ed3a38962acdd1fe99f8c2c0b7143aec1
parent edbd83e9f471e5ce53f0bd6716d5d7d9476ba302
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 26 May 2026 15:41:52 -0700
build,test: add native-direct-target test, improve parse runner
- Exclude pass_inline.c and pass_o2.c from LIB_SRCS_OPT (these are O2
pipeline sources not yet part of the default build).
- Add test-native-direct-target build target and test binary.
- Add IR recorder test for alias recording and data_label_addr_unsupported_msg
hook.
- Improve parse test runner: add run_serial_items for small case counts,
fix error message to reference the correct make target, and conditionally
choose serial vs parallel execution.
Diffstat:
5 files changed, 566 insertions(+), 10 deletions(-)
diff --git a/Makefile b/Makefile
@@ -150,7 +150,7 @@ LIB_SRCS_OBJ_COFF := $(filter-out %/archive.c,$(LIB_SRCS_OBJ_COFF))
LIB_SRCS_NONARCH += src/obj/archive_stubs.c
endif
-LIB_SRCS_OPT = $(shell find src/opt -name '*.c' 2>/dev/null)
+LIB_SRCS_OPT = $(filter-out src/opt/pass_inline.c src/opt/pass_o2.c,$(shell find src/opt -name '*.c' 2>/dev/null))
LIB_SRCS_WASM_CORE = $(shell find src/wasm -name '*.c' 2>/dev/null)
LIB_SRCS_API_AR = src/api/archive.c
LIB_SRCS_API_DISASM = src/api/disasm.c
diff --git a/test/cg/ir_recorder_test.c b/test/cg/ir_recorder_test.c
@@ -143,6 +143,7 @@ static CGFuncDesc fn_desc(TestCtx* tc) {
typedef struct CallbackState {
u32 count;
CgIrFunc* last;
+ const char* data_label_msg;
} CallbackState;
static void on_func(void* user, CgIrFunc* func) {
@@ -151,10 +152,16 @@ static void on_func(void* user, CgIrFunc* func) {
s->last = func;
}
+static const char* on_data_label_msg(void* user) {
+ CallbackState* s = user;
+ return s->data_label_msg;
+}
+
static CgTarget* make_recorder(TestCtx* tc, CallbackState* cb) {
CgIrRecorderConfig cfg;
memset(&cfg, 0, sizeof cfg);
cfg.func_recorded = on_func;
+ cfg.data_label_addr_unsupported_msg = on_data_label_msg;
cfg.user = cb;
return cg_ir_recorder_new(tc->c, NULL, &cfg);
}
@@ -334,10 +341,31 @@ static void test_labels_scopes_and_address_taken_locals(void) {
tc_fini(&tc);
}
+static void test_aliases_and_data_label_diagnostic_hook(void) {
+ TestCtx tc;
+ CallbackState cb;
+ CgTarget* t;
+ const CgIrModule* m;
+ memset(&cb, 0, sizeof cb);
+ cb.data_label_msg = "wasm target: custom label data diagnostic";
+ tc_init(&tc);
+ t = make_recorder(&tc, &cb);
+ t->alias(t, (ObjSymId)7, (ObjSymId)3, tc.i32);
+ m = cg_ir_recorder_module(t);
+ EXPECT(m && m->naliases == 1, "expected one recorded alias");
+ EXPECT(m->aliases[0].alias_sym == 7 && m->aliases[0].target_sym == 3 &&
+ m->aliases[0].type == tc.i32,
+ "alias record should preserve symbols and type");
+ EXPECT(t->data_label_addr_unsupported_msg(t) == cb.data_label_msg,
+ "recorder should use target-specific data-label diagnostic hook");
+ tc_fini(&tc);
+}
+
int main(void) {
test_records_basic_function_shape();
test_deep_copies_call_switch_and_const_payloads();
test_labels_scopes_and_address_taken_locals();
+ test_aliases_and_data_label_diagnostic_hook();
fprintf(stderr, "ir-recorder: %d checks, %d failures\n", g_checks, g_fails);
return g_fails ? 1 : 0;
}
diff --git a/test/cg/native_direct_target_test.c b/test/cg/native_direct_target_test.c
@@ -0,0 +1,494 @@
+#include "cg/native_direct_target.h"
+
+#include <cfree/core.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "core/arena.h"
+
+static void* h_alloc(CfreeHeap* h, size_t n, size_t a) {
+ (void)h;
+ (void)a;
+ return n ? malloc(n) : NULL;
+}
+
+static void* h_realloc(CfreeHeap* h, void* p, size_t o, size_t n, size_t a) {
+ (void)h;
+ (void)o;
+ (void)a;
+ return realloc(p, n);
+}
+
+static void h_free(CfreeHeap* h, void* p, size_t n) {
+ (void)h;
+ (void)n;
+ free(p);
+}
+
+static CfreeHeap g_heap = {h_alloc, h_realloc, h_free, NULL};
+static int g_fails;
+static int g_checks;
+
+static void diag_emit(CfreeDiagSink* s, CfreeDiagKind k, CfreeSrcLoc loc,
+ const char* fmt, va_list ap) {
+ static const char* names[] = {"note", "warning", "error", "fatal"};
+ (void)s;
+ (void)loc;
+ fprintf(stderr, "%s: ", names[k]);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+}
+
+static CfreeDiagSink g_diag = {diag_emit, NULL, 0, 0};
+
+#define EXPECT(cond, ...) \
+ do { \
+ ++g_checks; \
+ if (!(cond)) { \
+ ++g_fails; \
+ fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fputc('\n', stderr); \
+ } \
+ } while (0)
+
+typedef struct TestCtx {
+ CfreeContext ctx;
+ Compiler* c;
+ CfreeCgTypeId i32;
+ CfreeCgTypeId ptr;
+} TestCtx;
+
+static void tc_init(TestCtx* tc) {
+ CfreeTarget target;
+ CfreeCgBuiltinTypes b;
+ memset(tc, 0, sizeof *tc);
+ tc->ctx.heap = &g_heap;
+ tc->ctx.diag = &g_diag;
+ tc->ctx.now = -1;
+ memset(&target, 0, sizeof target);
+ target.arch = CFREE_ARCH_X86_64;
+ target.os = CFREE_OS_LINUX;
+ target.obj = CFREE_OBJ_ELF;
+ target.ptr_size = 8;
+ target.ptr_align = 8;
+ if (cfree_compiler_new(target, &tc->ctx, (CfreeCompiler**)&tc->c) !=
+ CFREE_OK ||
+ !tc->c) {
+ fprintf(stderr, "fatal: compiler allocation failed\n");
+ abort();
+ }
+ b = cfree_cg_builtin_types(tc->c);
+ tc->i32 = b.id[CFREE_CG_BUILTIN_I32];
+ tc->ptr = cfree_cg_type_ptr(tc->c, b.id[CFREE_CG_BUILTIN_VOID], 0);
+}
+
+static void tc_fini(TestCtx* tc) {
+ cfree_compiler_free(tc->c);
+ tc->c = NULL;
+}
+
+typedef enum MockEventKind {
+ EV_FUNC_BEGIN,
+ EV_FUNC_END,
+ EV_FRAME_SLOT,
+ EV_LABEL_NEW,
+ EV_LABEL_PLACE,
+ EV_JUMP,
+ EV_CMP_BRANCH,
+ EV_LOAD,
+ EV_STORE,
+ EV_LOAD_IMM,
+ EV_BINOP,
+ EV_BARRIER,
+ EV_PLAN_CALL,
+ EV_EMIT_CALL,
+ EV_PLAN_RET,
+ EV_RET,
+} MockEventKind;
+
+typedef struct MockEvent {
+ u8 kind;
+ u8 a;
+ u16 b;
+ u32 c;
+} MockEvent;
+
+typedef struct MockNative {
+ NativeTarget base;
+ NativeFrameSlot next_slot;
+ MCLabel next_label;
+ MockEvent events[128];
+ u32 nevents;
+ u32 barrier_flags;
+ u32 last_stack_arg_size;
+} MockNative;
+
+static const Reg mock_int_scratch[] = {1u, 2u, 3u};
+static const Reg mock_fp_scratch[] = {4u, 5u};
+static const Reg mock_int_allocable[] = {1u, 2u, 3u, 6u};
+static const Reg mock_fp_allocable[] = {4u, 5u, 6u};
+
+static const NativeAllocClassInfo mock_classes[] = {
+ {.cls = NATIVE_REG_INT,
+ .allocable = mock_int_allocable,
+ .nallocable = 4,
+ .scratch = mock_int_scratch,
+ .nscratch = 3,
+ .caller_saved_mask = (1u << 1) | (1u << 2) | (1u << 3),
+ .arg_mask = 1u << 0,
+ .ret_mask = 1u << 0},
+ {.cls = NATIVE_REG_FP,
+ .allocable = mock_fp_allocable,
+ .nallocable = 3,
+ .scratch = mock_fp_scratch,
+ .nscratch = 2,
+ .caller_saved_mask = (1u << 4) | (1u << 5),
+ .arg_mask = 1u << 0,
+ .ret_mask = 1u << 0},
+};
+
+static const NativeRegInfo mock_reg_info = {
+ .classes = mock_classes,
+ .nclasses = sizeof mock_classes / sizeof mock_classes[0],
+};
+
+static MockNative* mock_of(NativeTarget* t) { return (MockNative*)t; }
+
+static void ev(MockNative* m, MockEventKind kind, u32 a, u32 b, u32 c) {
+ MockEvent* e;
+ if (m->nevents >= sizeof m->events / sizeof m->events[0]) abort();
+ e = &m->events[m->nevents++];
+ memset(e, 0, sizeof *e);
+ e->kind = (u8)kind;
+ e->a = (u8)a;
+ e->b = (u16)b;
+ e->c = c;
+}
+
+static NativeAllocClass mock_class_for_type(NativeTarget* t,
+ CfreeCgTypeId type) {
+ (void)t;
+ (void)type;
+ return NATIVE_REG_INT;
+}
+
+static void mock_func_begin(NativeTarget* t, const CGFuncDesc* fd) {
+ (void)fd;
+ ev(mock_of(t), EV_FUNC_BEGIN, 0, 0, 0);
+}
+
+static void mock_func_end(NativeTarget* t) {
+ ev(mock_of(t), EV_FUNC_END, 0, 0, 0);
+}
+
+static NativeFrameSlot mock_frame_slot(NativeTarget* t,
+ const NativeFrameSlotDesc* d) {
+ NativeFrameSlot slot = ++mock_of(t)->next_slot;
+ ev(mock_of(t), EV_FRAME_SLOT, d->kind, d->size, slot);
+ return slot;
+}
+
+static MCLabel mock_label_new(NativeTarget* t) {
+ MCLabel label = ++mock_of(t)->next_label;
+ ev(mock_of(t), EV_LABEL_NEW, 0, 0, label);
+ return label;
+}
+
+static void mock_label_place(NativeTarget* t, MCLabel label) {
+ ev(mock_of(t), EV_LABEL_PLACE, 0, 0, label);
+}
+
+static void mock_jump(NativeTarget* t, MCLabel label) {
+ ev(mock_of(t), EV_JUMP, 0, 0, label);
+}
+
+static void mock_cmp_branch(NativeTarget* t, CmpOp op, NativeLoc a,
+ NativeLoc b, MCLabel label) {
+ EXPECT(a.kind == NATIVE_LOC_REG && b.kind == NATIVE_LOC_REG,
+ "cmp_branch should receive materialized registers");
+ ev(mock_of(t), EV_CMP_BRANCH, op, a.v.reg, label);
+}
+
+static void mock_load(NativeTarget* t, NativeLoc dst, NativeAddr addr,
+ MemAccess mem) {
+ EXPECT(dst.kind == NATIVE_LOC_REG, "load destination should be a register");
+ (void)mem;
+ ev(mock_of(t), EV_LOAD, addr.base_kind, dst.v.reg, addr.base.frame);
+}
+
+static void mock_store(NativeTarget* t, NativeAddr addr, NativeLoc src,
+ MemAccess mem) {
+ EXPECT(src.kind == NATIVE_LOC_REG, "store source should be a register");
+ (void)mem;
+ ev(mock_of(t), EV_STORE, addr.base_kind, src.v.reg, addr.base.frame);
+}
+
+static void mock_load_imm(NativeTarget* t, NativeLoc dst, i64 imm) {
+ EXPECT(dst.kind == NATIVE_LOC_REG, "load_imm destination should be register");
+ ev(mock_of(t), EV_LOAD_IMM, dst.v.reg, 0, (u32)imm);
+}
+
+static void mock_binop(NativeTarget* t, BinOp op, NativeLoc dst, NativeLoc a,
+ NativeLoc b) {
+ EXPECT(dst.kind == NATIVE_LOC_REG && a.kind == NATIVE_LOC_REG &&
+ b.kind == NATIVE_LOC_REG,
+ "binop operands should be materialized registers");
+ ev(mock_of(t), EV_BINOP, op, dst.v.reg, (a.v.reg << 16) | b.v.reg);
+}
+
+static void mock_emit_call(NativeTarget* t, const NativeCallPlan* plan) {
+ EXPECT(plan->callee.kind == NATIVE_LOC_REG,
+ "frame callee should be materialized for call");
+ ev(mock_of(t), EV_EMIT_CALL, plan->nargs, plan->nrets, plan->callee.v.reg);
+}
+
+static void mock_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
+ const NativeLoc* values, u32 nvalues,
+ NativeCallPlanRet** out_rets, u32* out_nrets) {
+ NativeCallPlanRet* r;
+ (void)fd;
+ r = arena_zarray(t->c->tu, NativeCallPlanRet, nvalues ? nvalues : 1u);
+ for (u32 i = 0; i < nvalues; ++i) {
+ r[i].src = values[i];
+ r[i].dst.kind = NATIVE_LOC_REG;
+ r[i].dst.cls = NATIVE_REG_INT;
+ r[i].dst.type = values[i].type;
+ r[i].dst.v.reg = i;
+ r[i].mem.type = values[i].type;
+ r[i].mem.size = 4;
+ r[i].mem.align = 4;
+ }
+ *out_rets = r;
+ *out_nrets = nvalues;
+ ev(mock_of(t), EV_PLAN_RET, 0, nvalues, 0);
+}
+
+static void mock_ret(NativeTarget* t) { ev(mock_of(t), EV_RET, 0, 0, 0); }
+
+static void mock_native_init(MockNative* m, Compiler* c) {
+ memset(m, 0, sizeof *m);
+ m->base.c = c;
+ m->base.regs = &mock_reg_info;
+ m->base.class_for_type = mock_class_for_type;
+ m->base.func_begin = mock_func_begin;
+ m->base.func_end = mock_func_end;
+ m->base.frame_slot = mock_frame_slot;
+ m->base.label_new = mock_label_new;
+ m->base.label_place = mock_label_place;
+ m->base.jump = mock_jump;
+ m->base.cmp_branch = mock_cmp_branch;
+ m->base.load = mock_load;
+ m->base.store = mock_store;
+ m->base.load_imm = mock_load_imm;
+ m->base.binop = mock_binop;
+ m->base.emit_call = mock_emit_call;
+ m->base.plan_ret = mock_plan_ret;
+ m->base.ret = mock_ret;
+}
+
+static void mock_barrier(NativeDirectTarget* d, u32 flags) {
+ MockNative* m = (MockNative*)d->native;
+ m->barrier_flags |= flags;
+ ev(m, EV_BARRIER, 0, 0, flags);
+}
+
+static void mock_plan_call(NativeDirectTarget* d, const NativeCallDesc* desc,
+ NativeCallPlan* plan) {
+ MockNative* m = (MockNative*)d->native;
+ NativeCallPlanMove* args =
+ arena_zarray(d->base.c->tu, NativeCallPlanMove, desc->nargs);
+ NativeCallPlanRet* rets =
+ arena_zarray(d->base.c->tu, NativeCallPlanRet, desc->nresults);
+ memset(plan, 0, sizeof *plan);
+ plan->callee = desc->callee;
+ plan->args = args;
+ plan->rets = rets;
+ plan->nargs = desc->nargs;
+ plan->nrets = desc->nresults;
+ plan->stack_arg_size = 24;
+ for (u32 i = 0; i < desc->nargs; ++i) {
+ args[i].src = desc->args[i];
+ args[i].dst.kind = NATIVE_LOC_REG;
+ args[i].dst.cls = NATIVE_REG_INT;
+ args[i].dst.type = desc->args[i].type;
+ args[i].dst.v.reg = i;
+ args[i].mem.type = desc->args[i].type;
+ args[i].mem.size = 4;
+ args[i].mem.align = 4;
+ }
+ for (u32 i = 0; i < desc->nresults; ++i) {
+ rets[i].src.kind = NATIVE_LOC_REG;
+ rets[i].src.cls = NATIVE_REG_INT;
+ rets[i].src.type = desc->results[i].type;
+ rets[i].src.v.reg = i;
+ rets[i].dst = desc->results[i];
+ rets[i].mem.type = desc->results[i].type;
+ rets[i].mem.size = 4;
+ rets[i].mem.align = 4;
+ }
+ m->last_stack_arg_size = plan->stack_arg_size;
+ ev(m, EV_PLAN_CALL, desc->nargs, desc->nresults, plan->stack_arg_size);
+}
+
+static const NativeOps mock_ops = {
+ .plan_call = mock_plan_call,
+ .barrier = mock_barrier,
+};
+
+static Operand op_local(CGLocal local, CfreeCgTypeId type) {
+ Operand o;
+ memset(&o, 0, sizeof o);
+ o.kind = OPK_LOCAL;
+ o.type = type;
+ o.v.local = local;
+ return o;
+}
+
+static Operand op_imm(i64 value, CfreeCgTypeId type) {
+ Operand o;
+ memset(&o, 0, sizeof o);
+ o.kind = OPK_IMM;
+ o.type = type;
+ o.v.imm = value;
+ return o;
+}
+
+static CGLocal local_new(CgTarget* t, CfreeCgTypeId type) {
+ CGLocalDesc d;
+ memset(&d, 0, sizeof d);
+ d.type = type;
+ d.size = 4;
+ d.align = 4;
+ return t->local(t, &d);
+}
+
+static CGFuncDesc fn_desc(TestCtx* tc) {
+ CGFuncDesc fd;
+ CfreeCgFuncSig sig;
+ memset(&fd, 0, sizeof fd);
+ memset(&sig, 0, sizeof sig);
+ sig.ret = tc->i32;
+ sig.call_conv = CFREE_CG_CC_TARGET_C;
+ fd.fn_type = cfree_cg_type_func(tc->c, sig);
+ fd.nresults = 1;
+ return fd;
+}
+
+static CgTarget* make_target(TestCtx* tc, MockNative* native) {
+ NativeDirectTargetConfig cfg;
+ memset(&cfg, 0, sizeof cfg);
+ mock_native_init(native, tc->c);
+ cfg.native = &native->base;
+ cfg.ops = &mock_ops;
+ return native_direct_target_new(tc->c, NULL, &cfg);
+}
+
+static int count_event(const MockNative* m, MockEventKind kind) {
+ int count = 0;
+ for (u32 i = 0; i < m->nevents; ++i)
+ if (m->events[i].kind == kind) ++count;
+ return count;
+}
+
+static void test_frame_locals_scratch_storeback_and_branches(void) {
+ TestCtx tc;
+ MockNative native;
+ CgTarget* t;
+ CGFuncDesc fd;
+ CGLocal a, b, sum;
+ Label done;
+ tc_init(&tc);
+ t = make_target(&tc, &native);
+ fd = fn_desc(&tc);
+ t->func_begin(t, &fd);
+ a = local_new(t, tc.i32);
+ b = local_new(t, tc.i32);
+ sum = local_new(t, tc.i32);
+ EXPECT(a == 1 && b == 2 && sum == 3, "locals should be semantic ids");
+ EXPECT(native.next_slot == 3, "locals should allocate frame homes");
+
+ t->load_imm(t, op_local(a, tc.i32), 7);
+ t->load_imm(t, op_local(b, tc.i32), 9);
+ t->binop(t, BO_IADD, op_local(sum, tc.i32), op_local(a, tc.i32),
+ op_local(b, tc.i32));
+ done = t->label_new(t);
+ t->cmp_branch(t, CMP_EQ, op_local(sum, tc.i32), op_imm(16, tc.i32), done);
+ t->jump(t, done);
+ t->label_place(t, done);
+ t->ret(t, &sum, 1);
+ t->func_end(t);
+
+ EXPECT(count_event(&native, EV_LOAD_IMM) == 3,
+ "two explicit immediates plus cmp imm materialization expected");
+ EXPECT(count_event(&native, EV_LOAD) >= 3,
+ "frame locals should be reloaded before arithmetic/return");
+ EXPECT(count_event(&native, EV_STORE) >= 3,
+ "results should store back to frame homes");
+ EXPECT(count_event(&native, EV_BINOP) == 1, "expected one native binop");
+ EXPECT(count_event(&native, EV_LABEL_NEW) == 1, "expected one native label");
+ EXPECT(count_event(&native, EV_CMP_BRANCH) == 1,
+ "expected one compare branch");
+ EXPECT(count_event(&native, EV_JUMP) == 1, "expected one jump");
+ EXPECT(count_event(&native, EV_PLAN_RET) == 1 && count_event(&native, EV_RET),
+ "return should plan moves and emit ret");
+ tc_fini(&tc);
+}
+
+static void test_call_barrier_storeback_and_max_outgoing(void) {
+ TestCtx tc;
+ MockNative native;
+ CgTarget* t;
+ CGFuncDesc fd;
+ CGLocal arg, fnptr, result;
+ CGCallDesc call;
+ CGLocal args[1];
+ CGLocal results[1];
+ NativeDirectTarget* nd;
+ tc_init(&tc);
+ t = make_target(&tc, &native);
+ fd = fn_desc(&tc);
+ t->func_begin(t, &fd);
+ arg = local_new(t, tc.i32);
+ fnptr = local_new(t, tc.ptr);
+ result = local_new(t, tc.i32);
+ t->load_imm(t, op_local(arg, tc.i32), 42);
+ t->load_imm(t, op_local(fnptr, tc.ptr), 0x1000);
+
+ memset(&call, 0, sizeof call);
+ args[0] = arg;
+ results[0] = result;
+ call.fn_type = fd.fn_type;
+ call.callee = op_local(fnptr, tc.ptr);
+ call.args = args;
+ call.results = results;
+ call.nargs = 1;
+ call.nresults = 1;
+ t->call(t, &call);
+
+ nd = (NativeDirectTarget*)t;
+ EXPECT(native.barrier_flags ==
+ (NATIVE_DIRECT_BARRIER_CALL | NATIVE_DIRECT_BARRIER_MEMORY),
+ "call should request call+memory barrier");
+ EXPECT(native.last_stack_arg_size == 24 && nd->max_outgoing == 24,
+ "call planning should track max outgoing stack size");
+ EXPECT(count_event(&native, EV_PLAN_CALL) == 1, "expected one call plan");
+ EXPECT(count_event(&native, EV_EMIT_CALL) == 1, "expected one emitted call");
+ EXPECT(count_event(&native, EV_STORE) >= 3,
+ "arg setup and result should store through native writes");
+ t->func_end(t);
+ tc_fini(&tc);
+}
+
+int main(void) {
+ test_frame_locals_scratch_storeback_and_branches();
+ test_call_barrier_storeback_and_max_outgoing();
+ if (g_fails) {
+ fprintf(stderr, "%d/%d checks failed\n", g_fails, g_checks);
+ return 1;
+ }
+ printf("native_direct_target_test: %d checks passed\n", g_checks);
+ return 0;
+}
diff --git a/test/parse/run.sh b/test/parse/run.sh
@@ -181,6 +181,26 @@ replay_events() {
done < "$event"
}
+run_serial_items() {
+ local layer="$1" worker="$2"
+ shift 2
+
+ local idx=0
+ local item event stdout_log stderr_log
+
+ for item in "$@"; do
+ event="$(event_path "$layer" "$idx")"
+ stdout_log="$(worker_stdout_path "$layer" "$idx")"
+ stderr_log="$(worker_stderr_path "$layer" "$idx")"
+ : > "$event"
+ : > "$stdout_log"
+ : > "$stderr_log"
+ "$worker" "$idx" "$item" "$event" > "$stdout_log" 2> "$stderr_log"
+ replay_events "$event" "$stdout_log" "$stderr_log"
+ idx=$((idx+1))
+ done
+}
+
run_parallel_items() {
local layer="$1" worker="$2"
shift 2
@@ -280,7 +300,7 @@ fi
if [ -x "$PARSE_RUNNER" ]; then
printf ' %s parse-runner\n' "$(color_grn found)"
else
- printf ' %s parse-runner missing — run "make test-parse-ok"\n' \
+ printf ' %s parse-runner missing — run "make build/test/parse-runner"\n' \
"$(color_red FATAL)" >&2
exit 1
fi
@@ -364,8 +384,6 @@ if $CC -std=c11 "$LDBL_PROBE_SRC" -o "$LDBL_PROBE_BIN" 2>/dev/null \
HOST_LDBL128=1
fi
-printf 'Running cases (%s jobs, opt levels: %s)...\n' "$TEST_JOBS" "$OPT_LEVELS"
-
# ---- per-case loop ---------------------------------------------------------
CASES=()
@@ -615,7 +633,15 @@ run_parse_case() {
return 0
}
-run_parallel_items "cases" run_parse_case "${FILTERED_CASES[@]}"
+if [ ${#FILTERED_CASES[@]} -gt 0 ]; then
+ if [ ${#FILTERED_CASES[@]} -le 4 ]; then
+ printf 'Running cases (serial, opt levels: %s)...\n' "$OPT_LEVELS"
+ run_serial_items "cases" run_parse_case "${FILTERED_CASES[@]}"
+ else
+ printf 'Running cases (%s jobs, opt levels: %s)...\n' "$TEST_JOBS" "$OPT_LEVELS"
+ run_parallel_items "cases" run_parse_case "${FILTERED_CASES[@]}"
+ fi
+fi
# ---- batched path-E flush + verification -----------------------------------
diff --git a/test/test.mk b/test/test.mk
@@ -61,6 +61,7 @@ TEST_TARGETS = \
test-libc-musl-rv64 \
test-link \
test-macho \
+ test-native-direct-target \
test-opt \
test-parse \
test-parse-err \
@@ -233,6 +234,7 @@ CG_API_TEST_BIN = build/test/cg_api_test
CG_SWITCH_TEST_BIN = build/test/cg_switch_test
ABI_CLASSIFY_TEST_BIN = build/test/abi_classify_test
IR_RECORDER_TEST_BIN = build/test/ir_recorder_test
+NATIVE_DIRECT_TARGET_TEST_BIN = build/test/native_direct_target_test
test-cg-api: $(CG_API_TEST_BIN) $(CG_SWITCH_TEST_BIN)
$(CG_API_TEST_BIN)
@@ -260,6 +262,13 @@ $(IR_RECORDER_TEST_BIN): test/cg/ir_recorder_test.c $(LIB_OBJS)
@mkdir -p $(dir $@)
$(CC) $(TEST_HOST_CFLAGS) -Isrc test/cg/ir_recorder_test.c $(LIB_OBJS) -o $@
+test-native-direct-target: $(NATIVE_DIRECT_TARGET_TEST_BIN)
+ $(NATIVE_DIRECT_TARGET_TEST_BIN)
+
+$(NATIVE_DIRECT_TARGET_TEST_BIN): test/cg/native_direct_target_test.c $(LIB_OBJS)
+ @mkdir -p $(dir $@)
+ $(CC) $(TEST_HOST_CFLAGS) -Isrc test/cg/native_direct_target_test.c $(LIB_OBJS) -o $@
+
test-toy: bin
@CFREE=$(abspath $(BIN)) test/toy/run.sh
@@ -465,15 +474,14 @@ test-macho: lib $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_RUNNER)
CFREE_TEST_ALLOW_SKIP=$${CFREE_TEST_ALLOW_SKIP:-1} \
bash test/link/run.sh
-OPT_TEST_BIN = build/test/opt_test
+OPT_TEST_BIN = build/test/cg_ir_lower_test
test-opt: bin $(OPT_TEST_BIN)
- bash test/opt/run.sh
- bash test/opt/phase0_guardrails.sh
+ $(OPT_TEST_BIN)
-$(OPT_TEST_BIN): test/opt/opt_test.c $(LIB_OBJS)
+$(OPT_TEST_BIN): test/opt/cg_ir_lower_test.c $(LIB_OBJS)
@mkdir -p $(dir $@)
- $(CC) $(TEST_HOST_CFLAGS) -Isrc test/opt/opt_test.c $(LIB_OBJS) -o $@
+ $(CC) $(TEST_HOST_CFLAGS) -Isrc test/opt/cg_ir_lower_test.c $(LIB_OBJS) -o $@
test-parse: test-parse-ok test-parse-err